最快的方法来检查一个string是否匹配正则expression式或不在ruby?
检查一个string是否与Ruby中的正则expression式匹配的最快方法是什么?
我的问题是,我必须通过一个巨大的string列表来“egrep”来查找哪些匹配在运行时给出的正则expression式。 我只关心string是否匹配正则expression式,而不是匹配的地方,匹配组的内容是什么。 我希望这个假设可以用来减less我的代码花费在匹配正则expression式上的时间。
我加载正则expression式
pattern = Regexp.new(ptx).freeze
我发现string =~ pattern
比string.match(pattern)
稍快。
还有其他的技巧或捷径可以用来使这个testing更快吗?
从Ruby 2.4.0开始,您可以使用RegExp#match?
:
pattern.match?(string)
Regexp#match?
在2.4.0的发行说明中被明确列为性能增强,因为它避免了由其他方法执行的对象分配,例如Regexp#match
和=~
:
正则expression式匹配#?
添加了正则Regexp#match?
,它执行一个正则expression式匹配而不创build一个返回引用对象,并改变$~
来减less对象分配。
这是一个简单的基准:
require 'benchmark' "test123" =~ /1/ => 4 Benchmark.measure{ 1000000.times { "test123" =~ /1/ } } => 0.610000 0.000000 0.610000 ( 0.578133) "test123"[/1/] => "1" Benchmark.measure{ 1000000.times { "test123"[/1/] } } => 0.718000 0.000000 0.718000 ( 0.750010) irb(main):019:0> "test123".match(/1/) => #<MatchData "1"> Benchmark.measure{ 1000000.times { "test123".match(/1/) } } => 1.703000 0.000000 1.703000 ( 1.578146)
所以=~
会更快,但是这取决于你想要的返回值。 如果你只是想检查文本是否包含正则expression式或不使用=~
这是我在网上find一些文章后运行的基准。
在2.4.0版本中,获胜者是re.match?(str)
(如@ wiktor-stribiżew所示),在以前的版本中, re =~ str
〜str似乎是最快的,尽pipestr =~ re
〜re几乎一样快。
#!/usr/bin/env ruby require 'benchmark' str = "aacaabc" re = Regexp.new('a+b').freeze N = 4_000_000 Benchmark.bm do |b| b.report("str.match re\t") { N.times { str.match re } } b.report("str =~ re\t") { N.times { str =~ re } } b.report("str[re] \t") { N.times { str[re] } } b.report("re =~ str\t") { N.times { re =~ str } } b.report("re.match str\t") { N.times { re.match str } } if re.respond_to?(:match?) b.report("re.match? str\t") { N.times { re.match? str } } end end
结果MRI 1.9.3-o551:
$ ./bench-re.rb | sort -t $'\t' -k 2 user system total real re =~ str 2.390000 0.000000 2.390000 ( 2.397331) str =~ re 2.450000 0.000000 2.450000 ( 2.446893) str[re] 2.940000 0.010000 2.950000 ( 2.941666) re.match str 3.620000 0.000000 3.620000 ( 3.619922) str.match re 4.180000 0.000000 4.180000 ( 4.180083)
结果MRI 2.1.5:
$ ./bench-re.rb | sort -t $'\t' -k 2 user system total real re =~ str 1.150000 0.000000 1.150000 ( 1.144880) str =~ re 1.160000 0.000000 1.160000 ( 1.150691) str[re] 1.330000 0.000000 1.330000 ( 1.337064) re.match str 2.250000 0.000000 2.250000 ( 2.255142) str.match re 2.270000 0.000000 2.270000 ( 2.270948)
结果MRI 2.3.3(在正则expression式匹配中有一个回归,看来):
$ ./bench-re.rb | sort -t $'\t' -k 2 user system total real re =~ str 3.540000 0.000000 3.540000 ( 3.535881) str =~ re 3.560000 0.000000 3.560000 ( 3.560657) str[re] 4.300000 0.000000 4.300000 ( 4.299403) re.match str 5.210000 0.010000 5.220000 ( 5.213041) str.match re 6.000000 0.000000 6.000000 ( 6.000465)
结果MRI 2.4.0:
$ ./bench-re.rb | sort -t $'\t' -k 2 user system total real re.match? str 0.690000 0.010000 0.700000 ( 0.682934) re =~ str 1.040000 0.000000 1.040000 ( 1.035863) str =~ re 1.040000 0.000000 1.040000 ( 1.042963) str[re] 1.340000 0.000000 1.340000 ( 1.339704) re.match str 2.040000 0.000000 2.040000 ( 2.046464) str.match re 2.180000 0.000000 2.180000 ( 2.174691)
怎么样re === str
(案例比较)?
由于它的计算结果是真或假,并且不需要存储匹配,返回匹配索引和那些东西,我不知道它是否比匹配更快的匹配方式。
好的,我testing了这个。 =~
仍然更快,即使您有多个捕获组,但是它比其他选项更快。
顺便说一句, freeze
什么好处? 我无法衡量任何性能提升。
根据你的正则expression式有多复杂,你可以使用简单的string切片。 我不确定这是否适用于您的应用程序,或者它是否会提供任何速度改进。
'testsentence'['stsen'] => 'stsen' # evaluates to true 'testsentence'['koala'] => nil # evaluates to false
我想知道的是,如果有什么奇怪的方式来使这个检查更快,也许利用一些奇怪的方法在正则expression式或一些奇怪的构造。
正则expression式引擎在实现search的方式上有所不同,但总的来说,锚定模式的速度并避免贪婪的匹配,特别是在search长string时。
在熟悉特定引擎的工作方式之前,最好的办法是做基准并添加/删除锚,尝试限制search,使用通配符与显式匹配等。
果味gem对于快速评测事物非常有用,因为它很聪明。 Ruby的内置的基准代码也是有用的,虽然你可以编写testing,愚弄你不小心。
我已经在堆栈溢出的许多答案中使用这两个,所以你可以通过我的答案进行search,并会看到很多小技巧和结果给你如何编写更快的代码的想法。
要记住的最大的问题是,在知道发生什么变化之前,过早地优化代码是不好的。
怎么样!(string !~ pattern)
?
没关系。 我testing了它。 它不比~=
快。