2016-06-08 4 views
3

m/.+?(\d{4})<\/i>\)/sПочему это регулярное выражение медленно?

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

EDIT: Вот пример кода, который демонстрирует проблему:

use WWW::Mechanize; 
my $mech = new WWW::Mechanize; 
$mech->get("http://www.elaws.gov.bw/desplaysubsidiary.php?m=SUBSIDIARY&v=I&vp=&id=904"); 
$page = $mech->content(); 
$page =~ m/.+?(\d{4})<\/i>\)/s; 

Регулярное выражение линия берет навсегда. Если я удалю .+?, нет задержки.

+3

Вы считали, что вместо этого используете законный парсер? – hwnd

+0

@hwnd Справедливая точка, но меня действительно интересует, почему такое простое регулярное выражение будет работать так медленно. – CJ7

+1

Все, что вам нужно, это 'm <(\d{4}) \)>'. Это быстрее? Если вы не видите улучшения, то это не регулярное выражение, которое медленное – Borodin

ответ

2

Это регулярное выражение медленно, потому что вы вводите ленивость в . с +?. Для 20-минутного html-привет мир он поднимает шаги с 60 (когда жадные) до 2000 (когда ленивый).

Представьте, что он будет делать с "некоторые нормальные HTML-страницы формата". Вы можете проверить here (под отладчик regex).

Также взгляните на why you shouldn't use regex to parse html.

+2

* "" Это регулярное выражение медленное, потому что вы вводите ленивость к '.' с' +? '" * Это неверно. «Ленивый» квантификатор начинается с сопоставления всего лишь возможно, и назад, путем расширения этого совпадения по одному символу за раз, пока остальная часть шаблона не завершится. «Нелистный» квантификатор делает обратное - он соответствует максимально возможному и обратному пути, сопоставляя один символ меньше, пока шаблон которые наиболее быстро зависят от того, совпадает ли оставшаяся часть шаблона с строкой объекта ближе к началу или концу. Ни одна из них не является медленной или быстрой по срокам – Borodin

+0

* «Для 20-минутного html-приветствия мир поднимает шаги от 60 (когда жадный) до 2000 года (когда ленивый) »* Я не уверен, что вы имеете в виду здесь. Вы говорите, что есть двадцать строк из 100 символов? Я не вижу, где вы получаете 60 от всего – Borodin

+3

Как сказал Бородин , вопрос не в том, что e квантификатор ленив. Переход на жадный квантификатор может быть медленнее, если есть совпадение в начале строки. OP, вероятно, хочет просто '/ (\ d {4}) <\/i> \)/s'. Кроме того, «не анализировать HTML с регулярным выражением» ответьте, что вы связаны с [ничего не объясняет] (http://meta.stackoverflow.com/q/261561); что-то вроде [this] (http://htmlparsing.com/regexes) гораздо полезнее. – ThisSuitIsBlackNot

5

Там, кажется, какое-то недоразумение об этом

Предположим, что у нас есть строка

my $s = 'xxxxxxxxxx9999</i>)'; 

затем шаблон совпадающий как этот

$s =~ m<.*?(\d{4})</i>\)> 

начнется, если предположить, что .*? занимает нет символов в начале строки. Затем он проверяет, соответствует ли (\d{4})</i>\) строке в этой точке

Он не работает, поэтому двигатель регулярного выражения дает один символ x до .*? и повторяет попытку. Это также терпит неудачу, поэтому часть строки, потребляемая .*?, расширена по-символу, пока она не соответствует десяти символам xxxxxxxxxx.В этот момент остаток шаблона соответствует успешно и тесту регулярного выражения объявляется успехом

Если вместо этого у нас есть, не ленив рисунок на

$s =~ m<.*(\d{4})</i>\)> 

Это запустит в предположении, что .* занимает все строки

Остальная часть рисунка не соответствует в этой точке, поэтому откат начинается за раз, давая .* все, кроме одного символа строки и повторите попытку

Это повторяется, как и раньше, но сокращение матча характер за символом, пока не будет найдено совпадение, когда он отступал над задними девяти символами строки 9999</i>) и .* теперь соответствует xxxxxxxxxx, как и раньше

Backtracking: Назад к ранее подобранному элементу шаблона, когда совпадение было найдено с ошибкой, изменяя, как этот элемент совпадает и повторяет попытку. Это не будет назад через объект строку ищет что-то

Проблема здесь вызвана .*?, которая должна учитываться в шаблоне. Если бы у нас было только m<(\d{4})</i>\)>, тогда вообще никакого возврата нет. Движок регулярных выражений просто ищет \d{4}</i>\) и либо находит, либо не

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

выше регулярное выражение медленно, когда я запускаю его через некоторый нормальный размер HTML-страниц?

Несмотря на это, в зависимости от того, какова ваша идея «HTML-страниц нормального размера», я не вижу, чтобы это заняло больше нескольких миллисекунд. Двигатель регулярных выражений закодирован в C и написан очень быстро. Думаю, вы должны запустить таймер, чтобы заметить какую-либо задержку?

+0

Итак, вы думаете, что с «нелазными» шаблонами он называется обратным следом, а для «ленивых» паттернов это называется ленивым расширением шаблона. – Borodin

+0

@ WiktorStribiżew: Процессы называются обратными трассировками. Единственное различие заключается в том, что откат в * ленивый * матч делает матч более длинным, в то время как откат в «нелатный» матч делает его короче. Пожалуйста, вы можете дать мне ссылку, которая поддерживает вашу идею? – Borodin

+1

@ WiktorStribiżew: Это очень сложно чтобы продемонстрировать негатив, но [Regular-Expressions.info] (http://www.regular-expressions.info/repeat.html) говорит об этом под заголовком «Лень вместо жадности» *** «Опять же, двигатель будет *** ** backtrack. ** *** Но на этот раз откат заставит ленивый плюс расширяться, а не уменьшать его охват ». *** – Borodin