Почему мы должны предпочесть отрицательные классы символов?* в регулярных выражениях?
Я смотрел учебник по регулярному выражению.
Речь шла о том, как получить атрибут класса из этого фрагмента html
<pre class="ruby" name="code">
И используемое регулярное выражение было
<pre class="([^"]+)" name="code">
Они рекомендовали использовать вышеприведенный вместо
<pre class="(.+)" name="code">
" как это выходит за рамки цитаты."
Я не понимаю, что они имеют в виду. Это просто будет работать в любом случае, но тогда почему рекомендуется первое регулярное выражение. Я что-нибудь упустил? Пожалуйста, просвети меня.Заранее благодарю.
4 ответа:
.+
спички жадно. например, в<pre class="ruby" size="medium" name="code">
Он будет соответствовать
ruby" size="medium
. Еще хуже, если бы у вас было два тега на одной строке, они совпадали бы прямо через границы тегов:<pre class="ruby" name="code">foo</pre> <pre class="python" name="code">bar</pre>
Приведет к
ruby" name="code">foo</pre> <pre class="python
!Так что пока вы точно знаете, как будет выглядеть ваш HTML,
.+
может работать, но как только он неожиданно изменится (как обычно делает HTML), ваше регулярное выражение не просто не сработает (как второе), но и будет соответствовать неправильному материалу.Следовательно, второе регулярное выражение является более безопасным (так как оно более четко определяет, что именно разрешено сопоставлять). Обычно вы должны стараться избегать простого
.+
или.*
"соответствовать чему-либо", а вместо этого думать о том, что вы действительно хотите соответствовать.Тем не менее, по тем же самым причинам, вы не должны пытаться сопоставлять HTML и другие языки разметки с регулярными выражениями в любом случае, потому что есть лучшие инструменты для этого.
Регулярное выражение обычно пытается соответствовать самому длинному регулярному выражению, которое оно может. Поэтому "([ ^ "]+) " соответствует только первой цитате , с которой она встречается. С другой стороны, " (.+ ) "будет соответствовать от первой цитаты до самой последней цитаты в строке.
Например, если мы применим их к вашему вопросу, первый будет соответствовать"ruby"
, потому что это первая цитируемая строка в вашем вопросе. Второй будет соответствовать всему пути от"ruby
доbeyond the quote"
, потому что это последний цитата в вопросе (и будет включать несколько других строк в кавычках между ними.
Рассмотрим следующий пример:
<pre class="scooby" name="not-code"> content </pre> ...other HTML... <pre class="ruby" name="code"> content </pre>
С этим регулярным выражением [*]:
<pre class="(.+)" name="code">
...первая часть -
<pre class="
- начинает соответствовать первому тегу, затем(.+)
потребляет всю остальную часть документа. Но остальная часть регулярного выражения -" name="code">
- не может соответствовать там, поэтому он отступает, пока не найдет позицию, где он может-во втором теге. Результат: группа в конечном итоге захватывает все отscooby
доruby
.Это будет верно, даже если вы используете не жадный
(.+?)
вместо жадного(.+)
. Люди часто говорят, что не жадные кванторы заставляют регулярное выражение возвращать кратчайшее возможное совпадение, но это не так. Подобно жадному регулярному выражению, оно начинает сопоставление при первой же возможности; оно простопрекращает сопоставление, как только может. Ситуации, подобные этой, когда не жадные кванторы не приносят никакой пользы, не редки.Еще одна вещь, о которой нужно подумать, - это когда совпадение невозможно,например, если есть теги
<pre>
с первым атрибутомclass="~whatever~"
, но нет ни одного с атрибутname="code"
. На каждом из них жадный(.+)
сожрет весь документ, а затем отступит, пока он не достигнет своей начальной точки, прежде чем сдаться. Ненасытный(.+?)
не будет возвращаться назад, но он будет сканировать всю страницу, и он будет делать это гораздо медленнее (он эффективно делает lookahead для" name="code">
в каждой позиции).С этим регулярным выражением:
<pre class="([^"]+)" name="code">
... он никогда не должен сканировать дальше конца тега, чтобы решить, соответствует ли он.
Всегда думай о том, что произойдет, если совпадение невозможно. Это, вероятно, самая распространенная ошибка авторов регулярных выражений, которая вызывает наибольшие проблемы с производительностью.
[*] я предполагаю, что матч выполняется в режиме DOTALL (он же однострочный режим) для иллюстрации.
Отрицание класса обычно гораздо более конкретно о том, что вы хотите соответствовать, и может помочь защитить от ситуаций, таких каккатастрофическое отступление .
Некоторое время назад Джефф Этвуд написал об этом интересное сообщение в блоге , где он приводит пример, казалось бы, невинного регулярного выражения:(x+x+)+y
, которое может занять (почти) вечность, чтобы закончить обработку. Даже когда предмет такой крошечный, как этот:xxxxxxxxxxxxxxxxxxxx
.Дайте ему прочитать, это действительно довольно интересно.