preg_match()和strpos()在匹配查找方面有何区别?

36

在进行单值检查时,哪种方法更好,为什么?

$string == 'The quick brown fox jumps over the lazy dog';

if(strpos($string, 'fox') !== false){
    // do the routine
}

# versus

if(preg_match('/fox/i', $string)){
    // do the routine
}

第一个看起来更短。使用strstr函数通常可以避免多余的结果检查。 - mario
3
你需要使用 stripos 来使得它们的功能一样。 - Wesley Murch
6个回答

72

我更倾向于使用 strpos 而不是 preg_match,因为正则表达式通常执行起来更耗费资源。

根据 preg_match 的官方 php 文档:

如果您只想检查一个字符串是否包含在另一个字符串中,请勿使用 preg_match()。相反,使用 strpos()strstr(),因为它们会更快。


4
哇,preg_match();的文档里有一个很重要的提示,但像往常一样,我没有注意到。好吧,感谢你把它复制粘贴到这里。我想这就是PHP团队亲自回答了我的问题。 - tomsseisums

13

当你不确定的时候,请进行基准测试!

显然,我们可以想出比这更好的基准测试,但是只是为了证明一点:随着规模的增长,strpos()将会快得多。(这里快了近2倍)

编辑 我后来注意到正则表达式是不区分大小写的。再次使用 stripos() 进行公平比较后,结果是 11 比 15,因此差距变小了,但 preg_match() 仍然慢得多。

$str = "the quick brown fox";
$start1 = time();
for ($i = 0; $i<10000000; $i++)
{
    if (strpos($str, 'fox') !== false)
    {
        //
    }
}
$end1 = time();
echo $end1 - $start1 . "\n";

$start2 = time();
for ($i = 0; $i<10000000; $i++)
{
    if (preg_match('/fox/i', $str))
    {
        //
    }
}
$end2 = time();
echo $end2 - $start2;

// Results:
strpos() = 8sec
preg_match() = 15sec

// Results both case-insensitive (stripos()):
stripos() = 11sec
preg_match() = 15sec

1
性能问题实际上是可选的,更关心的是使用情况。但还是谢谢! - tomsseisums
3
这种基准测试通常是无意义的,因为你甚至不会执行该函数“10000000”次,甚至不会执行“100”次。有时候,对于少量调用,函数a函数b快,反之亦然。更不用说依赖于timemicrotime是有问题的,它使用可能会变化的内部时钟。相反,我会使用[xdebug](http://xdebug.org/)。 - machineaddict
再说一遍简单的:if('fox' == $string)... 甚至更快。根据我的测试,这个代码(使用上面的代码)只需要3秒,而str_pos需要11秒,preg_match需要17秒。 - Richard - Rogue Wave Limited

8

除非绝对必要,否则不要使用正则表达式。在像这样的字符串上启动和部署正则表达式引擎所涉及的开销类似于使用大锤而不是普通的锤子,使用钻头而不是螺丝刀。

使用正则表达式还存在更大的错误率——不匹配的字符串、意外的结果等等。除非strpos不够灵活,否则请坚持使用它。


1
你是否有任何数字来支持并为正则表达式引擎的那些陈述提供背景? - rodrigo-silveira
3
我自从发布这个答案以来就没有再使用过PHP了,但当时我非常注重性能调优,并从Xdebug分析和观察许多写得很差的表达式中得出了这个结论,这些表达式是不必要地复杂的。然而,如果我现在重新编写这个答案,我不会那么极端了。我认为Cupcake的回答并引用PHP文档更加合适。 - glortho

6
如果您已经在代码中到处使用 preg_match 和 preg_replace,那么请继续使用它。为什么呢?
1.性能。这些函数增加的大部分开销都在引擎的初始加载时间中,如果您已经付出了代价,请让它有所作为。
2.可读性。strpos(...)!==false 虽然更快,但是非常难看。
它是最丑陋的 php 结构之一。 其中使用 == 和 false 是真正的修补和难以解析和编辑。
核心团队没有像 strcontains() 这样定义别名,真是太遗憾了,多年前就应该这样做了。 现在为时已晚,但当时这样做会很好。

1
我不认为因为一个函数看起来丑陋/不舒服就停止使用它。 - tomsseisums
4
在熟悉 PHP 内部类型转换的情况下,使用 !== false 是完全有道理的。 - Artefact2
1
@ZJR 我同意如果有一个更高级别的构造,比如 strcontains() 函数会更好。然而,我发现 strpos(...) !== falsepreg_match('...', $string) 这两个构造都同样丑陋。 - user456814
我不理解你关于strpos返回false是“眼中钉”的观点。相反,它应该返回false而不是0。它应该返回false而不是触发错误。返回值对客户端代码的任何使用都是实用的。 - Geo C.
就性能建议而言,实际上用于 ===、!==,也就是 JavaScript; - yardpenalty.com

3

好的代码更加重要

因此,如果您认为这种事情很重要,请记住它是Big O中的常数。换句话说,数据库调用、On2或更糟糕的活动是唯一重要的事情。在大多数情况下,花费时间担心这些低级命令是徒劳无功的。

并不是暗示应该忽略常数;例如,我重构了收集图像的代码,因为它一次只能收集一个图像,每个图像需要1秒钟,使用多curl请求将持续时间从12秒降至1秒。这个想法是内置命令是低级别的,而代码结构更加关键。

下面的代码进行了1000万次低级调用,你会发现“节省”非常微不足道。

function prof_flag($str)
{
    global $prof_timing, $prof_names;
    $prof_timing[] = microtime(true);
    $prof_names[] = $str;
}

function prof_print()
{
    global $prof_timing, $prof_names;
    $size = count($prof_timing);
    for($i=0;$i<$size - 1; $i++)
{
    echo "<b>{$prof_names[$i]}</b><br>";
        echo sprintf("&nbsp;&nbsp;&nbsp;%f<br>",     $prof_timing[$i+1]-$prof_timing[$i]);
    }
    echo "<b>{$prof_names[$size-1]}</b><br>";
}


$l = 10000000;
$str = "the quick brown fox";
echo "<h3>Ran " .number_format($l,2) ." calls per command </h3>";

prof_flag("Start: stripos");

for ($i = 0; $i<$l; $i++)
    if (stripos($str, 'fox') !== false) {}


prof_flag("Start: preg_match");

for ($i = 0; $i<$l; $i++)
    if (preg_match('#fox#i', $str) === 1) {}

prof_flag("Finished");
prof_print();

这段代码的唯一价值在于它展示了一个酷炫的记录运行时间的方法。

Ran 10,000,000.00 calls per command

Start: stripos
   2.217225
Start: preg_match
   3.788667
Start: ==
   0.511315
Start: ucwords lol
   2.112984
Finished

0

您可以通过编写以下代码来优化上述的preg_match

preg_match('/(?>fox)/', $str)

这应该更快。


3
你应该用一些词语解释为什么这更快。 - George G
比 preg_match 慢 - Mike Q

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接