You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 9-regular-expressions/15-regexp-infinite-backtracking-problem/article.md
+19-19Lines changed: 19 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,4 +1,4 @@
1
-
# Проблема поиска с бесконечным бэктрекингом
1
+
# Проблема поиска с бесконечным возвратом
2
2
3
3
Некоторые регулярные выражения, с виду являясь простыми, могут выполняться оооочень долго, и даже "подвешивать" интерпретатор JavaScript.
4
4
@@ -8,7 +8,7 @@
8
8
9
9
В веб-браузере такой случай убивает страницу. Явно плохая ситуация.
10
10
11
-
Такой код, выполняемый на стороне сервера, может стать уязвимостью, так как он использует регулярные выражения для обработки пользовательских данных. Некорректный ввод данных приведет к зависанию процесса и, как следствие, отказу сервиса. Автор лично видел и сообщал о таких уязвимостях даже для очень известных и широко используемых программ.
11
+
Ну а для серверного JavaScript это может стать серьёзной уязвимостью, так как регулярные выражения используются для обработки пользовательских данных. Некорректный ввод данных приведет к зависанию процесса и, как следствие, отказу сервиса. Автор лично видел и сообщал о таких уязвимостях даже для очень известных и широко используемых программ.
12
12
13
13
Так что проблема, несомненно, достойна рассмотрения.
14
14
@@ -22,11 +22,11 @@
22
22
23
23
Например, давайте рассмотрим поиск тегов в HTML.
24
24
25
-
Мы хотим найти все теги с атрибутами (или без них) типа: `subject:<a href="..." class="doc" ...>`. Нужно, чтобы регулярное выражение работало надёжно, так как HTML приходит из Интернета и может быть "грязным".
25
+
Мы хотим найти все теги с атрибутами (или без них) типа: `subject:<a href="..." class="doc" ...>`. Нужно, чтобы регулярное выражение работало надёжно, так как HTML приходит из Интернета и может быть некорректным.
26
26
27
-
В частности, чтобы регулярное выражение находило теги типа: `<a test="<>" href="#">` -- т.е. с символами `<` и `>` внутри атрибутов, так как это поддерживается [стандартом HTML](https://html.spec.whatwg.org/multipage/syntax.html#syntax-attributes).
27
+
В частности, нам нужно, чтобы регулярное выражение находило теги типа: `<a test="<>" href="#">` -- т.е. с символами `<` и `>` внутри атрибутов, так как это поддерживается [стандартом HTML](https://html.spec.whatwg.org/multipage/syntax.html#syntax-attributes).
28
28
29
-
Как видим, простое регулярное выражение `pattern:<[^>]+>` не работает, потому что оно останавливает поиск на первом `>`, а нам нужно игнорировать `<>`, если они являются частью атрибута.
29
+
Простое регулярное выражение `pattern:<[^>]+>` не работает, потому что оно останавливает поиск на первом `>`, а нам нужно игнорировать `<>`, если они являются частью атрибута.
Если мы подставим это в паттерн, описанный выше, и добавим дополнительные пробелы `pattern:\s`, то получим следующее: `pattern:<\w+(\s*\w+="[^"]*"\s*)*>`.
43
43
44
-
Это регулярное выражение неидеально! Оно всё ещё не поддерживает все детали HTML, например, значения в кавычках, и, хотя и есть способы улучшить его, давайте не будем его усложнять. Оно продемонстрирует нам проблему.
44
+
Это регулярное выражение неидеально! Оно не поддерживает все детали синтаксиса HTML, например, значения без кавычек, есть способы улучшить его, но давайте не будем его усложнять. Оно продемонстрирует нам проблему.
Поисковый движок снова должен отступить назад. В общем, бэктрекинг работает так: последний жадный квантификатор понижает количество повторений до тех пор, пока это возможно. Затем понижает предыдущий "жадный" квантификатор и т.д. В нашем случае последний "жадный" квантификатор -- это второй `pattern:\d+`, сокращающий `subject:89` до `subject:8`, а звёздочка берёт `subject:9`:
168
+
Поисковый движок снова должен отступить назад. В общем, возврат работает так: последний жадный квантификатор понижает количество повторений до тех пор, пока это возможно. Затем понижает предыдущий "жадный" квантификатор и т.д. В нашем случае последний "жадный" квантификатор -- это второй `pattern:\d+`, сокращающий `subject:89` до `subject:8`, а звёздочка берёт `subject:9`:
К сожалению, нет: если мы заменим `pattern:\d+` на `pattern:\d+?`, то регулярное выражение всё ещё будет "зависать" (осторожно! Может "подвесить" браузер):
В примере выше, когда в строке `subject:<a=b a=b a=b a=b` мы ищем теги по паттерну `pattern:<(\s*\w+=\w+\s*)*>`, происходит то же самое.
214
214
215
-
В конце строки нет `>`, поэтому совпадение невозможно, но движок не в курсе этого и, отступая, пробует другие комбинации `pattern:(\s*\w+=\w+\s*)`:
215
+
В конце строки нет `>`, поэтому совпадение невозможно, но движок не в курсе этого и, отступая, пробует другие комбинации `pattern:(\s*\w+=\w+\s*)`. Таких комбинаций много, поэтому это и занимает много времени.:
Другими словами, если мы нашли три пары `name=value`, а `>` после них найти не можем, то не нужно понижать число повторений. Последнего (`>`) точно нет после предыдущих двух пар `name=value` (мы "откатились" на одну пару `name=value`):
243
+
Другими словами, если мы нашли три пары `name=value`, а `>` после них найти не можем, то не нужно понижать число повторений. Последнего (`>`) точно нет после предыдущих двух пар `name=value` (мы "откатились" на одну пару `name=value`, там находится эта пара):
244
244
245
245
```
246
246
(name=value) name=value
247
247
```
248
248
249
-
В современных регулярных выражениях для решения этой проблемы придумали сверхжадные ("possessive") квантификаторы, которые вообще не используют бэктрегинг. То есть, они даже проще, чем "жадные" – берут максимальное количество символов и всё. Поиск продолжается дальше. Также есть "атомарные скобочные группы" -- средство, запрещающее перебор внутри скобок.
249
+
В современных регулярных выражениях для решения этой проблемы придумали сверхжадные ("possessive") квантификаторы, которые вообще не используют возврат. То есть, они даже проще, чем "жадные" – берут максимальное количество символов и всё. Поиск продолжается дальше. Также есть "атомарные скобочные группы" -- средство, запрещающее перебор внутри скобок.
250
250
251
251
К сожалению, в JavaScript они все не поддерживаются.
252
252
253
253
### Предпросмотр в помощь!
254
254
255
255
Но мы можем исключить бэктрекинг с помощью предпросмотра.
256
256
257
-
Паттерн, совершающий максимальное количество повторений без "отката", выглядит так: `pattern:(?=(a+))\1`.
257
+
Паттерн, совершающий максимальное количество повторений без возврата, выглядит так: `pattern:(?=(a+))\1`.
258
258
259
259
Другими словами:
260
260
- Предпросмотр `pattern:?=` ищет максимальное количество `pattern:a+`, доступных с текущей позиции.
261
261
- А затем они "берутся в результат" обратной ссылкой `pattern:\1` (`pattern:\1` соответствует содержимому вторых скобок, т.е. `pattern:a+`)
262
262
263
-
Откат в этой логике в принципе не предусмотрен, поскольку предпросмотр "откатываться" не умеет. То есть, если предпросмотр нашёл 5 `pattern:a+`, и в результате поиск не удался, то он не будет откатываться на 4 повторения.
263
+
Возврат в этой логике в принципе не предусмотрен, поскольку предпросмотр "откатываться" не умеет. То есть, если предпросмотр нашёл 5 `pattern:a+`, и в результате поиск не удался, то он не будет откатываться на 4 повторения.
264
264
265
265
```smart
266
-
Больше о взаимодействиях сверхжадных квантификаторов и предпросмотра вы можете найти в статьях [Regex: Emulate Atomic Grouping (and Possessive Quantifiers) with LookAhead](http://instanceof.me/post/52245507631/regex-emulate-atomic-grouping-with-lookahead) и [Mimicking Atomic Groups](http://blog.stevenlevithan.com/archives/mimic-atomic-groups).
266
+
Больше о связи между сверхжадных квантификаторов и предпросмотра вы можете найти в статьях [Regex: Emulate Atomic Grouping (and Possessive Quantifiers) with LookAhead](http://instanceof.me/post/52245507631/regex-emulate-atomic-grouping-with-lookahead) и [Mimicking Atomic Groups](http://blog.stevenlevithan.com/archives/mimic-atomic-groups).
0 commit comments