在 perl 中检查 $string 是不是以 $needle 开头的最有效方法

Posted

技术标签:

【中文标题】在 perl 中检查 $string 是不是以 $needle 开头的最有效方法【英文标题】:Most efficient way to check if $string starts with $needle in perl在 perl 中检查 $string 是否以 $needle 开头的最有效方法 【发布时间】:2015-10-21 20:29:33 【问题描述】:

给定perl 中的两个字符串变量$string$needle,检查$string 是否以$needle 开头的最有效方法是什么。

$string =~ /^\Q$needle\E/ 是我能想到的最接近的匹配项,但它是我尝试过的解决方案中效率最低的(到目前为止)。 index($string, $needle) == 0 工作并且对于 $string$needle 的某些值相对有效,但在其他位置不必要地搜索针(如果在开始时没有找到)。 substr($string, 0, length($needle)) eq $needle 应该是相当简单和高效的,但在我的少数测试中,大部分都没有比上一个更高效。

perl 中是否有一种我不知道的规范方法或任何优化上述解决方案的方法?

(在我的特定用例中,$string$needle 在每次运行中都会有所不同,因此预编译正则表达式不是一种选择)。


如何衡量给定解决方案性能的示例(此处来自 POSIX sh):

string='somewhat not so longish string' needle='somew'
time perl -e '
  ($n,$string,$needle) = @ARGV;
  for ($i=0;$i<$n;$i++) 

    index($string, $needle) == 0

  ' 10000000 "$string" "$needle"

有了这些值,index() 在这个带有 perl 5.14.2 的系统中的性能优于 substr()+eq,但是:

string="aaaaabaaaaabaaaaabaaaaabaaaaabaaaaab" needle="aaaaaa"

反过来了。

【问题讨论】:

不同版本的 Perl 会在这里产生影响,我建议添加您用于反馈或重用的基准代码。 @Ashley,好点,更新了。 也许你会写String::MoreUtils::XS 您是否对脚本进行了分析以确认确实需要这种微优化? 仅供参考,您应该在任何时候使用 Benchmark module 在 Perl 中进行基准测试。 /usr/bin/time 不一定会给你一个公平的比较。 【参考方案1】:
rindex $string, $substring, 0

$string 中搜索$substring 只有在$substring$string 的前缀时才有可能在 位置。示例:

> rindex "abc", "a", 0
0
> rindex "abc", "b", 0
-1

【讨论】:

非常感谢。这是我不知道并且正在寻找的功能。对于问题中的两个测试用例,我得到了相似的时间安排,并且比这两种方法中的任何其他方法都快。【参考方案2】:

这真的很重要吗?我做了一些基准测试,index 方法平均每次迭代需要 0.68 微秒;正则表达式方法1.14μs; substr 方法 0.16μs。即使是我的最坏情况(2250 个字符的字符串相等),index 需要 2.4μs,正则表达式需要 5.7μs,substr 需要 0.5μs。

我的建议是编写一个库例程:

sub begins_with

    return substr($_[0], 0, length($_[1])) eq $_[1];

并将您的优化工作集中在其他地方。

更新:基于对上述“最坏情况”场景的批评,我使用随机生成的 20,000 个字符的字符串运行了一组新的基准测试,将其与自身和仅在最后一个不同的字符串进行比较字节。

对于这么长的字符串,正则表达式解决方案是迄今为止最差的(20,000 个字符的正则表达式是地狱):匹配成功需要 105μs,匹配失败需要 100μs。

indexsubstr 解决方案仍然相当快。 index 是 11.83μs / 11.86μs 成功/失败,substr 是 4.09μs / 4.15μs。将代码移动到单独的函数中增加了大约 0.222±0.05μs。

基准代码位于:http://codepaste.net/2k1y8e

我不知道@Stephane 数据的特点,但我的建议是成立的。

【讨论】:

对于早期的perls,您可能想要omit the return statement。 不是没用的,@ikegami。我的基准案例中有一半是匹配的,一半是匹配失败的。 @SueD.Nymme:您发布的答案的措辞暗示您的最坏情况测试仅匹配字符串。显然,index 的最坏情况是一个极长的干草堆,任何地方都没有包含针头,因此它必须一直检查到最后。不过,我同意你的结论:只需使用substr,因为我们已经证明它在常见情况下并不慢。它应该有一个好多更好的最坏情况,这对于抵抗 DOS 攻击(或意外减速)很重要。 您可以尝试重现它们,而不是简单地忽略我的基准测试结果。 @PeterCordes,在字符串中找不到针的情况中,有些情况比其他情况更糟,例如问题中的最后一个示例,其中至少 111 个字节到字节的比较((6+5+4+3+2+1)*5+6) 需要长度为 34 的字符串和长度为 6 的针。(对于这个长度的字符串/针,它甚至可能是最坏的情况,这会在这里提出另一个有趣的问题)

以上是关于在 perl 中检查 $string 是不是以 $needle 开头的最有效方法的主要内容,如果未能解决你的问题,请参考以下文章

如何检查 Perl 标量变量是不是已初始化?

如何检查 Perl 的哈希中是不是存在密钥? [复制]

如何在 Perl 中检查多个变量是不是为空

perl 测试内容以查看是不是包含 HTML

检查一个分叉的孩子是不是已经在 perl 中执行

如何检查我是不是在 Perl 中下载了整个文件?