Javascript регулярное выражение зависает (с использованием v8) - PullRequest
9 голосов
/ 09 марта 2010

Я использую это регулярное выражение для получения содержимого тега в файле.

var regex = new RegExp("<tag:main>((?:.|\\s)*)</tag:main>");

Это приводит к зависанию двигателя v8.

Теперь, если я использую new RegExp("<tag:main>([\s\S]*)</tag:main>"), все хорошо.

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

Ответы [ 3 ]

16 голосов
/ 09 марта 2010

Это катастрофически возвращает назад к длинным последовательностям пробелов, которые появляются после последнего закрывающего тега </tag:main>.Рассмотрим случай, когда строка темы заканчивается 100 пробелами.Сначала он сопоставляет их со знаком . слева от чередования.Это терпит неудачу, потому что нет закрывающего тега, поэтому он пытается сопоставить последний символ с \s.Это тоже не удается, поэтому он пытается сопоставить второй с последним пробел как \s и последний пробел как ..Это терпит неудачу (все еще без закрывающего тега), поэтому он пробует последний пробел как \s.Если это не удается, он соответствует пробелу от третьего до последнего как \s и пробует все 4 способа сопоставить последние два пробела.Когда это не удается, он пробует четвертый-последний пробел как \s и все 8 путей в последних 3 пробелах.Затем 16, 32 и т. Д. Вселенная заканчивается до того, как достигает 100-го-последнего пространства.

Разные виртуальные машины по-разному реагируют на совпадения регулярных выражений, которые происходят вечно из-за катастрофического возврата.Некоторые просто сообщат «нет совпадения».В V8 это похоже на написание любого другого бесконечного или почти бесконечного цикла.

Использование не жадного * будет делать то, что вы хотите (вы хотите остановиться на первом </tag:main>, а не на последнем), нобудет по-прежнему выполнять катастрофическое обратное отслеживание для длинных строк пробелов, в которых отсутствует закрывающая последовательность.

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

3 голосов
/ 09 марта 2010

Полагаю, что это катастрофически обратно отслеживание.

Я думаю, что отчасти проблема в том, что точка и \ s не являются взаимоисключающими.

Если я поменяю выражение твоего лица на

<tag:main>((?:.|[\r\n])*)</tag:main>

и запустите его в отладчике Regex Buddy, и он провалится гораздо быстрее, если тестовая строка не совпадает.

0 голосов
/ 07 февраля 2015

Вместо (?:.|\s)* вы можете использовать [^]* для сопоставления с любым символом, включая различные формы новой строки.

Чередования нет, поэтому нет риска катастрофического возврата.

...