2011-11-29 7 views
3

Недавно я начал использовать регулярное выражение проверки подлинности электронной почты из плагина JQuery validation в моделях Rails.Регулярное выражение плохо работает в Ruby по сравнению с JavaScript

EMAIL_REGEXP=/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i 

"[email protected]".match EMAIL_REGEXP # returns immidiately 
"[email protected]".match EMAIL_REGEXP # takes a long time 

Регулярное выражение занимает много времени, когда недействительный адрес электронной почты имеет много точек, разделенные лексемы (например: [email protected]). То же выражение works without любая заметная задержка в JavaScript.

Почему существует такая разница в производительности между парсерами регулярного выражения Ruby и JavaScript? Есть ли что-то, что я могу сделать, чтобы улучшить время отклика?

Я на рубине 1.8.7. Я не вижу такой же проблемы в Ruby 1.9.2.

Примечание

Я знаю, что р-ехр долго. Поскольку он используется jQuery, я думал об использовании его. Я всегда могу изменить его на более простое регулярное выражение, как показано на рисунке here. Мой вопрос в основном заключается в выяснении причины того, что одно и то же регулярное выражение намного быстрее в JS.

Ссылка:

JQuery Validation Plugin Source

Sample form with jQuery email validation

+2

Это одно огромное регулярное выражение. Почему вы не используете парсер электронной почты? – Blender

+0

@ Качество комментариев в комментарии будет значительно улучшаться, если вы предоставили ссылку – tekknolagi

+2

Ну, это регулярное выражение взято из официального плагина 'jQuery' и широко используется. Поэтому я подумал, что это безопасный маршрут. Я могу пойти по пути пользовательской проверки, если я не могу заставить RegExp работать. –

ответ

1

Не знаю, почему парсер regex из 1.8.7 работает намного медленнее, чем тот, что от JS или Oniguruma от 1.9.2, но может быть это конкретное регулярное выражение может извлечь выгоду из упаковки его префикса, включая символ @ с атомной группой, например что:

EMAIL_REGEXP =/
^
    (?>((# atomic group start 
    ([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+ 
    (\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)* 
    ) 
    | 
    (
    (\x22) 
    (
     (((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)? 
     (
     ([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]) 
     | 
     (\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])) 
     ) 
    )* 
    (((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)? 
    (\x22) 
    ) 
) 
    @) # atomic group end 
    (
    (
     ([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]) 
     | 
     (
     ([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]) 
     ([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])* 
     ([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]) 
    ) 
    ) 
    \. 
)+ 
    (
    ([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]) 
    | 
    (
     ([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]) 
     ([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])* 
     ([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]) 
    ) 
) 
    $ 
    /xi 

puts "[email protected]".match EMAIL_REGEXP # returns immediately 
puts "[email protected]".match EMAIL_REGEXP # takes a long time 

Atomic группа в этом случае должна препятствовать парсер от возвращения к первой части строки при совпадении части следующей @ символ не удался. И это дает значительное ускорение. Хотя, я не уверен на 100%, что он не нарушает логику регулярных выражений, поэтому я буду благодарен за любые комментарии.

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

1

Проблема может быть в том, что ваш Regexp содержит жадных кванторов, поэтому Руби, как эти кванторы требуют пытаясь проверить все комбинации. Решение может заключаться в использовании Possessive Quantifiers, поэтому поиск будет быстрее, но он изменит регулярное выражение, чтобы некоторые строки больше не совпадали. Короткий пример (из Википедии):

'aaaaaaaaaaaaaaaaaaaaaaaaa' =~ /(a+a+)/ => match 
'aaaaaaaaaaaaaaaaaaaaaaaaa' =~ /(a++a+)/ => not match 

differense находится в процессе поиска и жадных кванторы двигателя пытается выглядеть-обратно, если не совпадает, то в случае притяжательных кванторов двигатель никогда не оглядывается.

+0

Я ожидал, что regexp будет медленнее из-за жадных квантификаторов. Меня удивляет разница в производительности между временем выполнения JS и Ruby для одного и того же reg-exp. –

+1

Я попробовал ваш exmaple в [tryruby] (http://tryruby.org/), и не было больших задержек с выполнением кода. Кстати, вторая строка dosnt соответствует. –

+0

Tryruby использует 1.9.2, который имеет новый механизм регулярных выражений и отлично справляется с этим регулярным выражением. С другой стороны, 1.8.7 просто умирает во втором примере из вопроса. –

Смежные вопросы