Самый быстрый способ проверить, соответствует ли строка регулярному выражению или нет в ruby?



каков самый быстрый способ проверить, соответствует ли строка регулярному выражению в Ruby?



моя проблема заключается в том, что я должен "egrep" через огромный список строк, чтобы найти те, которые соответствуют регулярному выражению, которое задается во время выполнения. меня волнует только то, соответствует ли строка регулярному выражению, а не то, где она соответствует, и что такое содержимое соответствующих групп. Я надеюсь, что это предположение может быть использовано для уменьшения времени, которое мой код тратит на сопоставление регулярное выражение.



я загружаю регулярное выражение с



pattern = Regexp.new(ptx).freeze


Я нашел это string =~ pattern немного быстрее, чем string.match(pattern).



есть ли другие трюки или ярлыки, которые могут использоваться, чтобы сделать этот тест еще быстрее?

656   7  

7 ответов:

начиная с Ruby 2.4.0, вы можете использовать RegExp#match?:

pattern.match?(string)

Regexp#match? явно указан как повышение производительности в примечания к выпуску для 2.4.0, так как это позволяет избежать выделения объектов, выполняемых другими методами, такими как Regexp#match и =~:

регулярное выражение матч#?
Добавлено Regexp#match?, который выполняет регулярное выражение соответствия без создания объекта обратной ссылки и изменения $~ для уменьшения объекта распределение.

Это простой тест:

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)

Так =~ быстрее, но это зависит от того, что вы хотите иметь в качестве возвращаемого значения. Если вы просто хотите проверить, содержит ли текст регулярное выражение или не использовать =~

это тест, который я запустил после поиска некоторых статей по сети.

С 2.4.0 победитель re.match?(str) (как предложил @wiktor-stribiżew), в предыдущих версиях,re =~ str кажется, самый быстрый, хотя str =~ 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

результаты МРТ 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)

результаты МРТ 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)

результаты МРТ 2.3.3 (есть регрессия в регулярном выражении соответствия, кажется):

$ ./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)

результаты МРТ 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 (дело сравнивать)?

поскольку он оценивает true или false и не имеет необходимости хранить совпадения, возвращая индекс соответствия и т. д., Мне интересно, будет ли это еще более быстрый способ сопоставления, чем =~.


Ок, я проверил это. =~ еще быстрее, даже если у вас несколько групп захвата, однако это быстрее, чем другие варианты.

кстати, что хорошего freeze? Я не мог измерить повышение производительности от оно.

в зависимости от того, насколько сложно ваше регулярное выражение, Вы можете просто использовать простое строковое нарезание. Я не уверен в практичности этого для вашего приложения или в том, действительно ли он будет предлагать какие-либо улучшения скорости.

'testsentence'['stsen']
=> 'stsen' # evaluates to true
'testsentence'['koala']
=> nil # evaluates to false

Мне интересно, есть ли какой-то странный способ сделать эту проверку еще быстрее, возможно, используя какой-то странный метод в регулярном выражении или какую-то странную конструкцию.

механизмы регулярных выражений различаются тем, как они реализуют поиск, но, как правило, привязывают ваши шаблоны к скорости и избегают жадных совпадений, особенно при поиске длинных строк.

лучшее, что можно сделать, пока вы не знакомы с тем, как работает конкретный двигатель, это сделать тесты и добавьте / удалите якоря, попробуйте ограничить поиск, используйте подстановочные знаки против явных совпадений и т. д.

на фруктовое gem очень полезен для быстрого бенчмаркинга вещей, потому что он умный. Рубин встроенный Benchmark код также полезен, хотя вы можете писать тесты, которые обманывают вас, не будучи осторожными.

я использовал оба во многих ответах здесь на Stack Overflow, так что вы можете искать через мои ответы и увидите много маленьких трюков и результатов, чтобы дать вам идеи о том, как писать более быстрый код.

самое главное, чтобы помнить, что это плохо, чтобы преждевременно оптимизировать код, прежде чем вы знаете, где происходят замедления.

чтобы завершить ответы Виктора Стрибижева и Дугуи, я бы сказал, что /regex/.match?("string") немного быстрее, чем "string".match?(/regex/).

2.4.0 > require 'benchmark'
 => true 
2.4.0 > Benchmark.measure{ 10000000.times { /^CVE-[0-9]{4}-[0-9]{4,}$/.match?("CVE-2018-1589") } }
 => #<Benchmark::Tms:0x005563da1b1c80 @label="", @real=2.2060338060000504, @cstime=0.0, @cutime=0.0, @stime=0.04000000000000001, @utime=2.17, @total=2.21> 
2.4.0 > Benchmark.measure{ 10000000.times { "CVE-2018-1589".match?(/^CVE-[0-9]{4}-[0-9]{4,}$/) } }
 => #<Benchmark::Tms:0x005563da139eb0 @label="", @real=2.260814556000696, @cstime=0.0, @cutime=0.0, @stime=0.010000000000000009, @utime=2.2500000000000004, @total=2.2600000000000007> 

Comments

    Ничего не найдено.