在 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。
index
和 substr
解决方案仍然相当快。 index
是 11.83μs / 11.86μs 成功/失败,substr
是 4.09μs / 4.15μs。将代码移动到单独的函数中增加了大约 0.222±0.05μs。
基准代码位于:http://codepaste.net/2k1y8e
我不知道@Stephane 数据的特点,但我的建议是成立的。
【讨论】:
对于早期的perl
s,您可能想要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 开头的最有效方法的主要内容,如果未能解决你的问题,请参考以下文章