|
@@ -181,6 +181,135 @@ p><b>Tproger</b> — мой <i>любимый</i> сайт о программи
|
|
|
|
|
|
|
|
Это происходит из-за того, что по умолчанию квантификатор работают по т.н. "жадному" алгоритму — старается вернуть как можно более длинную строку, соответствующую условию. Решить проблему можно двумя способами. Первый — использовать выражение `<[^>]*>`, которое запретит считать содержимым тега правую угловую скобку. Второй — объявить квантификатор не "жадным", а "ленивым". Делается это с помощью добавления справа к квантификатору символа "?". Т.е. для поиска всех тегов выражение обратится в `<.*?>`.
|
|
Это происходит из-за того, что по умолчанию квантификатор работают по т.н. "жадному" алгоритму — старается вернуть как можно более длинную строку, соответствующую условию. Решить проблему можно двумя способами. Первый — использовать выражение `<[^>]*>`, которое запретит считать содержимым тега правую угловую скобку. Второй — объявить квантификатор не "жадным", а "ленивым". Делается это с помощью добавления справа к квантификатору символа "?". Т.е. для поиска всех тегов выражение обратится в `<.*?>`.
|
|
|
|
|
|
|
|
|
|
+## Опережающие и ретроспективные проверки
|
|
|
|
|
+
|
|
|
|
|
+В некоторых случаях нам нужно найти соответствия шаблону, но только те, за которыми или перед которыми следует другой шаблон.
|
|
|
|
|
+
|
|
|
|
|
+Для этого в регулярных выражениях есть специальный синтаксис: опережающая и ретроспективная проверка.
|
|
|
|
|
+
|
|
|
|
|
+В качестве первого примера найдём стоимость из строки `1 индейка стоит 30€`. То есть, найдём число, после которого есть знак валюты `€`.
|
|
|
|
|
+
|
|
|
|
|
+### Опережающая проверка
|
|
|
|
|
+
|
|
|
|
|
+Синтаксис опережающей проверки: `X(?=Y)`.
|
|
|
|
|
+
|
|
|
|
|
+Он означает: найди `X` при условии, что за ним следует `Y`. Вместо `X` и `Y` здесь может быть любой шаблон.
|
|
|
|
|
+
|
|
|
|
|
+Для целого числа, за которым идёт знак `€`, шаблон регулярного выражения будет `\d+(?=€)`:
|
|
|
|
|
+
|
|
|
|
|
+```js
|
|
|
|
|
+let str = "1 индейка стоит 30€";
|
|
|
|
|
+alert( str.match(/\d+(?=€)/) ); // 30, число 1 проигнорировано, так как за ним НЕ следует €
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Обратим внимание, что проверка – это именно проверка, содержимое скобок `(?=...)` не включается в результат `30`.
|
|
|
|
|
+
|
|
|
|
|
+При поиске `X(?=Y)` движок регулярных выражений, найдя `X`, проверяет есть ли после него `Y`. Если это не так, то игнорирует совпадение и продолжает поиск дальше.
|
|
|
|
|
+
|
|
|
|
|
+Возможны и более сложные проверки, например `X(?=Y)(?=Z)` означает:
|
|
|
|
|
+
|
|
|
|
|
+1. Найти `X`.
|
|
|
|
|
+1. Проверить, идёт ли `Y` сразу после `X` (если нет – не подходит).
|
|
|
|
|
+1. Проверить, идёт ли `Z` сразу после `X` (если нет – не подходит).
|
|
|
|
|
+1. Если обе проверки прошли – совпадение найдено.
|
|
|
|
|
+
|
|
|
|
|
+То есть этот шаблон означает, что мы ищем `X` при условии, что за ним идёт и `Y` и `Z`.
|
|
|
|
|
+
|
|
|
|
|
+Такое возможно только при условии, что шаблоны `Y` и `Z` не являются взаимно исключающими.
|
|
|
|
|
+
|
|
|
|
|
+Например, `\d+(?=\s)(?=.*30)` ищет `\d+` при условии, что за ним идёт пробел, и где-то впереди есть `30`:
|
|
|
|
|
+
|
|
|
|
|
+```js
|
|
|
|
|
+let str = "1 индейка стоит 30€";
|
|
|
|
|
+
|
|
|
|
|
+alert( str.match(/\d+(?=\s)(?=.*30)/) ); // 1
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+В нашей строке это как раз число 1.
|
|
|
|
|
+
|
|
|
|
|
+### Негативная опережающая проверка
|
|
|
|
|
+
|
|
|
|
|
+Допустим, нам нужно узнать из этой же строки количество индеек, то есть число `\d+`, за которым НЕ следует знак `€`.
|
|
|
|
|
+
|
|
|
|
|
+Для этой задачи мы можем применить негативную опережающую проверку.
|
|
|
|
|
+
|
|
|
|
|
+Синтаксис: `X(?!Y)`
|
|
|
|
|
+
|
|
|
|
|
+Он означает: найди такой `X`, за которым НЕ следует `Y`.
|
|
|
|
|
+
|
|
|
|
|
+```js
|
|
|
|
|
+let str = "2 индейки стоят 60€";
|
|
|
|
|
+
|
|
|
|
|
+alert( str.match(/\d+(?!€)/) ); // 2 (в этот раз проигнорирована цена)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### Ретроспективная проверка
|
|
|
|
|
+
|
|
|
|
|
+Опережающие проверки позволяют задавать условия на то, что «идёт после».
|
|
|
|
|
+
|
|
|
|
|
+Ретроспективная проверка выполняет такую же функцию, но с просмотром назад. Другими словами, она находит соответствие шаблону, только если перед ним есть что-то заранее определённое.
|
|
|
|
|
+
|
|
|
|
|
+Синтаксис:
|
|
|
|
|
+
|
|
|
|
|
+* Позитивная ретроспективная проверка: `(?<=Y)X`, ищет совпадение с `X` при условии, что перед ним ЕСТЬ `Y`.
|
|
|
|
|
+* Негативная ретроспективная проверка: `(?<!Y)X`, ищет совпадение с `X` при условии, что перед ним НЕТ `Y`.
|
|
|
|
|
+
|
|
|
|
|
+Чтобы протестировать ретроспективную проверку, давайте поменяем валюту на доллары США. Знак доллара обычно ставится перед суммой денег, поэтому для того чтобы найти `$30`, мы используем `(?<=\$)\d+` – число, перед которым идёт `$`:
|
|
|
|
|
+
|
|
|
|
|
+```js
|
|
|
|
|
+let str = "1 индейка стоит $30";
|
|
|
|
|
+
|
|
|
|
|
+// знак доллара экранируем \$, так как это специальный символ
|
|
|
|
|
+alert( str.match(/(?<=\$)\d+/) ); // 30, одинокое число игнорируется
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Если нам необходимо найти количество индеек – число, перед которым не идёт `$`, мы можем использовать негативную ретроспективную проверку `(?<!\$)\d+`:
|
|
|
|
|
+
|
|
|
|
|
+```js
|
|
|
|
|
+let str = "2 индейки стоят $60";
|
|
|
|
|
+
|
|
|
|
|
+alert( str.match(/(?<!\$)\d+/) ); // 2 (проигнорировалась цена)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### Скобочные группы
|
|
|
|
|
+
|
|
|
|
|
+Как правило, то что находится внутри скобок, задающих опережающую и ретроспективную проверку, не включается в результат совпадения.
|
|
|
|
|
+
|
|
|
|
|
+Например, в шаблоне `\d+(?=€)` знак `€` не будет включён в результат. Это логично, ведь мы ищем число `\d+`, а `(?=€)` – это всего лишь проверка, что за ним идёт знак `€`.
|
|
|
|
|
+
|
|
|
|
|
+Но в некоторых ситуациях нам может быть интересно захватить и то, что в проверке. Для этого нужно обернуть это в дополнительные скобки.
|
|
|
|
|
+
|
|
|
|
|
+В следующем примере знак валюты `(€|kr)` будет включён в результат вместе с суммой:
|
|
|
|
|
+
|
|
|
|
|
+```js
|
|
|
|
|
+let str = "1 индейка стоит 30€";
|
|
|
|
|
+let regexp = /\d+(?=(€|kr))/; // добавлены дополнительные скобки вокруг €|kr
|
|
|
|
|
+
|
|
|
|
|
+alert( str.match(regexp) ); // 30, €
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+То же самое можно применить к ретроспективной проверке:
|
|
|
|
|
+
|
|
|
|
|
+```js
|
|
|
|
|
+let str = "1 индейка стоит $30";
|
|
|
|
|
+let regexp = /(?<=(\$|£))\d+/;
|
|
|
|
|
+
|
|
|
|
|
+alert( str.match(regexp) ); // 30, $
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### Итого
|
|
|
|
|
+
|
|
|
|
|
+Опережающая и ретроспективная проверки удобны, когда мы хотим искать шаблон по дополнительному условию на контекст, в котором он находится.
|
|
|
|
|
+
|
|
|
|
|
+Виды проверок:
|
|
|
|
|
+
|
|
|
|
|
+Шаблон | Тип | Совпадение
|
|
|
|
|
+:-----:|-----|-----------
|
|
|
|
|
+`X(?=Y)` | Позитивная опережающая | `X`, если за ним следует `Y`
|
|
|
|
|
+`X(?!Y)` | Негативная опережающая | `X`, если за ним НЕ следует `Y`
|
|
|
|
|
+`(?<=Y)X` | Позитивная ретроспективная | `X`, если следует за `Y`
|
|
|
|
|
+`(?<!Y)X` | Негативная ретроспективная | `X`, если НЕ следует за `Y`
|
|
|
|
|
+
|
|
|
## Регулярки в C#
|
|
## Регулярки в C#
|
|
|
|
|
|
|
|
Основная функциональность регулярных выражений в .NET сосредоточена в пространстве имен *System.Text.RegularExpressions*. А центральным классом при работе с регулярными выражениями является класс **Regex**. Например, у нас есть некоторый текст и нам надо найти в нем все словоформы какого-нибудь слова. С классом **Regex** это сделать очень просто:
|
|
Основная функциональность регулярных выражений в .NET сосредоточена в пространстве имен *System.Text.RegularExpressions*. А центральным классом при работе с регулярными выражениями является класс **Regex**. Например, у нас есть некоторый текст и нам надо найти в нем все словоформы какого-нибудь слова. С классом **Regex** это сделать очень просто:
|