Как предотвратить регулярное выражение зависания (или установить время ожидания для него) в .Net - PullRequest
2 голосов
/ 12 мая 2009

Я использую регулярное выражение для удаления тега комментария в HTML-файле (Шаблон: "<!--(.|\s)*?--!?>")

Но некоторые сайты не используют стандартный тег HTML, образец:

<script language="javascript">
    <!-- 
     js code ...
    </script>

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

Ответы [ 2 ]

10 голосов
/ 14 мая 2009

Проблема с производительностью вашего регулярного выражения является тривиальной. Не делай этого:

(.|\s)*

Является ли квантификатор ленивым или жадным, совершенно не имеет значения. Проблема в том, что . и \ s не являются взаимоисключающими. Пробелы могут быть сопоставлены с обоими. и \ с. Таким образом, если ваше регулярное выражение встречается с пробелом, оно сначала будет соответствовать пробелу с., А если остаток регулярного выражения завершится ошибкой, он снова сопоставит его с \ s. Если у вас есть два пробела, он сначала будет совпадать с., Затем с первым. и второй с \ s, затем первый с \ s и второй с., а затем оба с \ s. Как вы можете видеть, у вашего регулярного выражения есть сложность O (2 ^ N), когда он сталкивается с серией пробелов, за которыми следует что-то, что остальная часть регулярного выражения не может соответствовать. Если у вас есть 10 пробелов, есть 1024 перестановок. Если у вас 32 пробела, существует 4 миллиарда перестановок.

Причина, по которой вы видите проблему только тогда, когда ваше регулярное выражение терпит неудачу, состоит в том, что когда регулярное выражение успешно, то. просто соответствует всем пробелам, и \ s никогда не получает никакого действия.

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

.*

Если вы не можете установить RegexOptions.SingleLine, используйте этот модификатор режима, чтобы сделать то же самое:

(?s).*

Если вы не можете использовать этот модификатор режима, например, JavaScript не поддерживает его, используйте символьный класс с двумя дополнительными сокращениями:

[\S\s]*

Как только вы получите это ужасное (. | \ S) изменение из своего регулярного выражения, оно будет работать отлично. Нет необходимости использовать какие-либо сложные регулярные выражения, предложенные другими. Один ленивый квантификатор всегда расширяется линейно. Чередование, которое не является взаимоисключающим, всегда убивает ваше регулярное выражение. Я действительно называю это катастрофическим отступлением .

И если вам нужно регулярное выражение, позволяющее тегу завершить комментарий, попробуйте следующее:

(?s)<!--.*?(-->|</script>)
2 голосов
/ 12 мая 2009

Вы можете переписать регулярное выражение так, чтобы оно не работало как можно быстрее, когда совпадение невозможно, например:

<!--(?>(?:[^-]+|-(?!->))*)-->

Если за нераскрытым комментарием в вашем примере последует полный комментарий, это регулярное выражение будет соответствовать от первого <!-- до первого -->, например:

<!-- blah <!-- blah -->

Вот как ваш браузер должен обрабатывать комментарии SGML. Фактически, если не найдено совпадений -->, все после <!-- закомментировано. Таким образом, регулярное выражение должно быть:

<!--(?>(?:[^-]+|-(?!->))*)(?:-->|\z)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...