Содержание

Урок №5. Исключение определённых символов | Регулярные выражения

  Обновл. 11 Ноя 2019  | 

В некоторых случаях мы можем знать наперёд, что есть определённые символы, которые мы не хотим сопоставлять, например, мы хотим найти все телефонные номера, которые не заканчиваются на 00.

Для этого мы можем использовать выражение, которое исключает определённые символы, используя квадратные скобки и символ ^. Например, шаблон [^abc] будет соответствовать любому одиночному символу, кроме букв a, b или c.

В задании ниже попробуйте написать шаблон, который соответствует только живым животным (hog, dog, но не bog). Обратите внимание, большинство шаблонов этого типа также могут быть написаны и с использованием концепции из предыдущего урока. Имея два возможных варианта решения, вы можете выбирать, какой из них проще и понятнее реализовать.

Задание №5: Исключение символов

СоответствоватьhogTo be completed
СоответствоватьdogTo be completed
ПропуститьbogTo be completed
РешениеСамым простым решением для сопоставления любой строки, которая заканчивается на og, но не является bog, было бы выражение [^b]og. В качестве альтернативы, вы можете использовать вариант решения из предыдущего урока и использовать [hd]og для сопоставления hog и dog, но не bog. Обратите внимание, что это (второе) решение имеет ограничение в количестве строк для соответствия.

Решите задание выше, чтобы перейти к следующему уроку, либо смотрите Решение.

Оценить статью:

To be completed Загрузка…

Поделиться в социальных сетях:

Имитируем пересечение, исключение и вычитание, с помощью опережающих проверок, в регулярных выражениях в ECMAScript

От переводчика

Это перевод небольшой заметки, написанной вчера Lea Verou, в ней предлагается интересная, хотя и не новая техника для решения повседневных задач.

Информация в статье касается ECMAScript, но может использоваться и в других RegExp Движках (хотя и есть вероятность, что там есть более подходящее решение).

Если примеры кажутся вам сложными, рекомендую играть с ними в консоли, по мере прочтения. И Заранее прощу прочтение за пугающее название.

Статья

Если вы какое-то время используете регулярные выражения, то наверняка вы сталкивались с разными вариантами следующих задач:

  • Пересечение:«Что-то, что совпадает с шаблоном А и шаблоном Б»

    Например: Пароль, минимум 6 символов, в котором хотя бы одна цифра, хотя бы одна буква, и хотя бы один специальный символ
  • Исключение: «Я хочу что-то, что совпадает с шаблоном А, но не совпадает с шаблоном Б»

    Например: Любое целое число, которое не делится на 50
  • Отрицание: Все. Что не совпадает с шаблоном А

    Например: Строка, которая не содержит в себе слово «Foo»

Несмотря на то, что в ECMAScript есть циркумфлекс (^), для исключения набора символов, у нас нету возможности, исключить что-то более сложное.

Кроме того, у нас и есть вертикальная черта (|) обозначающая «ИЛИ», но нас нету ничего, что обозначало бы «И», и ничего что обозначало бы «КРОМЕ» (Исключение). Можно проделать все эти действия с несложным набором символов, с помощью символьных классов, но с со сложными последовательностями такое не получится.

Тем не менее, мы можем имитировать все три операции, воспользовавшись тем, что опережающие проверки не захватывают символы и не сдвигают позицию поиска. Мы можем просто продолжить искать соответствие дальше, и они будут совпадать с нужной нам подстрокой, поскольку опережающие проверки не захватывает ничего…

Исключение

В качестве простого примера: выражение /^(?! мяу)\w{3}$/ захватит любое слово из трех символов, не содержащее слово «мяу». Это очеть простой вариант исключения.

Вот решение для задачи, предложенной выше: /^(?!\d+[50]0)\d{3}$/.

Пересечение

Для пересечения (И), мы просто можем выстроить в цепочку несколько позитивных опережающих проверок, а последним шаблоном захватить нужную нам строку (Если оставить только опережающие проверки, то мы все равно получим верный ответ, но можем получить неверные соответствия). Например, решение для задачи с паролем, приведенной выше, будет таким: /^(?=.*\d)(?=.*[a-z])(?=.*[\W_]).{6,}$/i.

Если вы хотите, чтобы ваши регулярные выражения работали в Internet Explorer 8 версии и ниже, важно знать об этой ошибке и изменить ваши регулярные выражения соответствнно

Отрицание

Отрицание — проше всего. Нам всего лишь нужно негативное опережающие условие и .+, чтобы захватить подстроку, прошедшую проверку. Например, решение для предложенной выше задачи, будет выглядеть так: /^(?!.*foo).+$/. Стоит, правда, признать, что из всего списка, отрицание неименее полезно

Заключение

В этой технике присутствуют некоторые сложности. В основном это связано с тем, что захватывается в результате. (Убедитесь, что захватывающий шаблон вне опережающих проверок захватывают всю строку, которая вам нужна)

Steven Levithan копает еще глубже, и пытается имитировать операторы условия и атомарные группы. Прощай мозг.

Пара бонусных ссылок

Утилита , разбирающая регулярные выражения по частям и объясняющая их
JS библиотека, значительно облегчающая работу с регулярными выражениями и добавляющая им функционал.

Регулярные выражения за 15 минут

  1. Доступные статьи

  2. IT-шное

  3. Регулярные выражения за 15 минут

Регулярные выражения (regular expressions) — это текстовый шаблон, который соответствует какому-то тексту. И всё? Да, это всё, для чего они нужны.

Что можно делать с помощью регулярных выражений:

  • Проверять то, что вводит пользователь, чтобы быть уверенным в правильности данных (например, правильно ли пользователь ввёл email или ip-адрес).
  • Разбирать большой текст на меленькие кусочки (например, выбирать данные из большого лога).
  • Делать замены по шаблону (например, убирать непечатаемые символы из XML).
  • Показывать невероятную крутость тем, кто не знает регулярных выражений.

Большинство современных языков программирования и текстовых редакторов (по моему личному мнению) поддерживают регулярные выражения. Поддержим их и мы.

/Быть или не быть/ugi ¶

Синтаксис регулярных выражений прост и логичен. Он разделяется на символ-разделитель (он идёт в начале и конце выражения, обычно это /), шаблон поиска и необязательные модификаторы.

Формальный синтаксис такой:

[разделитель][шаблон][разделитель][модификаторы]

Разделителем может быть любой символ, но обычно это / или ~. Важно лишь то, чтобы шаблон начинался и заканчивался одним и тем же разделителем. В самом конце регулярных выражений идут модификаторы, которые нужны, чтобы менять логику работы шаблонов (например делать регистронезависимый поиск).

Давайте разберём выражение /Быть или не быть/ugi:

/                - начальный символ-разделитель
Быть или не быть - шаблон поиска
/                - конечный символ-разделитель
ugi              - модификаторы (UTF-8, global, case insensitive)

Данное регулярное выражение будет искать текст Быть или не быть не зависимо от регистра по всему тексту неограниченное количество раз. Модификатор u нужен для того, чтобы явно указать, что текст у нас в юникоде, то есть содержит символы, отличные от латиницы. Модификатор i включает регистронезависимый поиск. Модификатор g указывает поисковику идти до победного конца, иначе он остановится после первого удачного совпадения.

«Петя любит Дашу».replace(/Дашу|Машу|Сашу/, «Катю») ¶

Не трудно догадаться, что результатом работы js-выражения выше будет текст "Петя любит Катю". Даже, если Петя неровно дышит к Маше или Саше, то результат всё равно не изменится.

Рассмотрим базовые спец. символы, которые можно использовать в шаблонах:

СимволОписаниеПример использованияРезультат
\Символ экранирования или начала мета-символа/путь\/к\/папке/Надёт текст путь/к/папке
^Признак начала строки/^Дом/Найдёт все строки, которые начинаются на Дом
$Признак конца строки/родной$/Найдёт все строки, которые заканчиваются на родной
.Точка означает любой символ, кроме перевода строки/Петя ..бит Машу/Найдёт как Петя любит Машу, так и Петя губит Машу
|Означает ИЛИ/Вася|Петя/Найдёт как Васю, так и Петю
?Означает НОЛЬ или ОДИН раз/Вжу?х/Найдёт Вжх и Вжух
*Означает НОЛЬ или МНОГО раз/Вжу*х/Найдёт Вжх, Вжух, Вжуух, Вжууух и т.д.
+Означает ОДИН или МНОГО раз/Вжу+х/Найдёт Вжух, Вжуух, Вжууух и т.д.

Помимо базовых спец. символов есть мета-символы (или мета-последовательности), которые заменяют группы символов:

СимволОписаниеПример использованияРезультат
\wБуква, цифра или _ (подчёркивание)/^\w+$/Соответствует целому слову без пробелов, например _Вася333_
\WНЕ буква, цифра или _ (подчёркивание)/\W\w+\W/Найдёт полное слово, которое обрамлено любыми символами, например @Петя@
\dЛюбая цифра/^\d+$/Соответствует целому числу без знака, например 123
\DЛюбой символ НЕ цифра/^\D+$/Соответствует любому выражению, где нет цифр, например Петя
\sПробел или табуляция (кроме перевода строки)/\s+/Найдёт последовательность пробелов от одного и до бесконечности
\SЛюбой символ, кроме пробела или табуляции/\s+\S/Найдёт последовательность пробелов, после которой есть хотя бы один другой символ
\bГраница слова/\bдом\b/Найдёт только отдельные слова дом, но проигнорирует рядом
\BНЕ граница слова/\Bдом\b/Найдёт только окночние слов, которые заканчиваются на дом
\RЛюбой перевод строки (Unix, Mac, Windows)/.*\R/Найдёт строки, которые заканчиваются переводом строки

Нужно отметить, что спец. символы \w, \W, \b и \B не работают по умолчанию с юникодом (включая кириллицу). Для их правильной работы нужно указывать модификатор u. К сожалению, на окончание 2019 года JavaScript не поддерживает регулярные выражения для юникода даже с модификатором, поэтому в js эти мета-символы работают только для латиницы.

Ещё регулярные выражения поддерживают разные виды скобочек:

ВыражениеОписаниеПример использованияРезультат
(…)Круглые скобки означают под-шаблон, который идёт в результат поиска/(Петя|Вася|Саша) любит Машу/Найдёт всю строку и запишет воздыхателя Маши в результат поиска под номером 1
(?:…)Круглые скобки с вопросом и двоеточием означают под-шаблон, который НЕ идёт в результат поиска/(?:Петя|Вася|Саша) любит Машу/Найдёт только полную строку, воздыхатель останется инкогнито
(?P<name>…)Задаёт имя под-шаблона/(?P<воздыхатель>Петя|Вася|Саша) любит Машу/Найдёт полную строку, а воздыхателя запишет в результат под индексом 1 и ‘воздыхатель’
[abc]Квадратные скобки задают ЛЮБОЙ СИМВОЛ из последовательности (включая спец. символы \w, \d, \s и т.д.)/^[123]+$/Соответствует любому выражению 323323123, но не 54321
[a-я0-9]Если внутри квадратных скобок указать минус, то это считается диапазоном/[A-Za-zА-Яа-яЁё0-9_]+/Аналог /\w/ui для JavaScript
[abc-]Если минус является первым или последним символом диапазона, то это просто минус/[0-9+-]+/Найдёт любое целое числое с плюсом или минусом (причём не обязательно, чтобы минус или плюс были спереди)
[^…]Квадратные скобки с «крышечекой» означают любой символ НЕ входящий в диапазон/[^a-zа-я0-9 ]/iНайдёт любой символ, который не является буквой, числом или пробелом
[[:class:]]Квадратные скобки в квадратных скобках задают класс символов (alnum, alpha, ascii, digit, print, space, punct и другие)/[^[:print:]]+/Найдёт последовательность непечатаемых символов
{n}Фигурные скобки с одним числом задают точное количество символов/\w+н{2}\w+/uНайдёт слово, в котором две буквы н
{n,k}Фигурные скобки с двумя числами задают количество символов от n до k/\w+н{1,2}\w+/uНайдёт слово, в котором есть одна или две буквы н
{n,}Фигурные скобки с одним числом и запятой задают количество символов от n до бесконечности/\w+н{3,}\w+/uНайдёт слово, в котором н встречается от трёх и более раз подряд

Как правильно писать регулярные выражения ¶

Прежде, чем садиться и писать регулярно выраженного кракена, подумайте, что именно вы хотите сделать. Регулярное выражение должно начинаться с мысли «Я хочу найти/заменить/удалить то-то и то-то». Затем вам нужен исходный текст, который содержит как ПРАВИЛЬНЫЕ, так и НЕправильные данные. Затем вы открываете https://regex101.com/, вставляете текст и начинаете писать регулярное выражение. Этот замечательный инструмент укажет и покажет все ошибки, а также подсветит результаты поиска.

Для примера возьмём валидацию ip-адреса. Первая мысль должна быть: «Я хочу валидировать ip-адрес. А что такое ip-адрес? Из чего он состоит?». Затем нужен список валидных и невалидных адресов:


0.0.0.0 
0.1.2.3
99.99.99.99
199.199.199.199
255.255.255.255


01.01.01.01
.1.2.3
1.2.3.
255.0.0.256

Валидный адрес должен содержать четыре числа (байта) от 0 до 255. Если он содержит число больше 255, это уже ошибка. Если бы мы делали валидацию на каком-либо языке программирования, то можно было бы разбить выражение на четыре части и проверить каждое число отдельно. Но регулярные выражения не поддерживают проверки больше или меньше, поэтому придётся делать по-другому.

Для начала упростим задачу: будем валидировать не весь ip-адрес, а только один байт. А байт это всегда есть либо одно-, либо дву-, либо трёхзначное число. Для одно- и двузначного числа шаблон очень простой — любая цифра. А вот для трёхзначного числа первая цифра либо единица, либо двойка. Если первая цифра единица, то вторая и третья могут быть от нуля до девяти. Если же первая цифра двойка, то вторая может быть только от нуля до пяти. Если первая цифра двойка и вторая пятёрка, то третья может быть только от ноля до пяти. Давайте формализуем:


от 0 до 9              \d
от 10 до 99            [1-9]\d
от 100 до 199          1\d\d
от 200 до 249          2[0-4]\d
от 250 до 255          25[0-5]

Теперь, зная все диапазоны байта, можно объединить их в одно выражение через вертикальную палочку | (ИЛИ):

\b(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\b

Обратите внимание, что я использовал границу слова \b, чтобы искать полные байты. Пробуем регулярку в деле:

Как видим, все байты стали зелёненькими. Это значит, что мы на верном пути.

Осталось дело за малым: сделать так, чтобы искать четыре байта, а не один. Нужно учесть, что байты разделены тремя точками. То есть мы ищем три байта с точкой на конце и один без точки:

(\b(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\b\.){3}\b(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\b

Результат выглядит так:

Подсветились только валидные ip-адреса, значит регулярное выражение работает корректно.

Если бы я сразу начал писать валидацию всего адреса, а не отдельного байта, то с большой долей вероятности допустил бы ошибку. Скопления скобочек, палочек и точечек трудно воспринимаются на глаз, поэтому задачу надо обязательно упрощать.

Практическое применение регулярных выражений ¶

Регулярными выражениями можно пользоваться не только для валидации, но и для обработки данных, например, в блокноте. Вот практический пример такой обработки: скопировать номера регионов и перевести в формат PHP-массива.

Your browser does not support HTML5 video.

Ссылки ¶

Хорошая статья, мне понравилась. Оставлю отзыв!
Что-то статья не очень. Поругаю-ка я автора.


© 2020 Антон Прибора. При копировании материалов с сайта, пожалуйста, указывайте ссылку на источник.

Регулярные выражения, пособие для новичков. Часть 2 / Хабр

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

Больше метасимволов

Есть некоторые метасимволы, которые мы еще не изучили. Большинство из них будут рассмотрены в этом разделе.

Некоторые из оставшихся метасимволов являются утверждениями нулевого размера. Они не вызывают движок для прохода по строке, они вообще не охватывают никаких символов, возможен просто успех или неудача. Например, \b это утверждение о том, что текущая позиция находится на границе (boundary) слова, при этом сам символ \b не изменяет позицию. Это означает, что утверждения нулевого размера никогда не должны повторяться, потому что, если они совпали один раз в данном месте, они, очевидно, будут соответствовать этому месту бесконечное число раз.

|

Соответствует оператору ИЛИ. Если А и В являются регулярными выражениями, то A|B будет соответствовать любая строка, которая соответствует А или В. Метасимвол | имеет очень низкий приоритет для того, чтобы заставить его работать разумно, когда вы чередуете несколько символов строки. Crow|Servo будет искать соответствие либо Crow, либо Servo, а не Cro('w' или 'S')ervo.

^

Ищет соответствие только в начале строки. Если включен флаг MULTILINE, как говорилось в прошлой части, то происходит сравнение и для каждой части после символа новой строки.

Например, если вы хотите найти только те строки, у которых в начале имеется From, то в регулярном выражении записывается ^From:

>>> print re.search(‘^From’, ‘From Here to Eternity’)
<_sre.SRE_Match object at 0x…>
>>> print re.search(‘^From’, ‘Reciting From Memory’)
None

$

То же, что ^, но в конце строки, которая определяется либо, собственно по концу строки как таковому, либо по символу новой строки.

>>> print re.search(‘}$’, ‘{block}’)
<_sre.SRE_Match object at 0x…>
>>> print re.search(‘}$’, ‘{block} ‘)
None
>>> print re.search(‘}$’, ‘{block}\n’)
<_sre.SRE_Match object at 0x…>

\A

Совпадение только в начале строки, то есть тоже, что ^, но не зависит от флага MULTILINE

\Z

Совпадение только в конце строки, то есть тоже, что $, но не зависит от флага MULTILINE

\b

Граница слова. Слово определяется как последовательность символов чисел и/или букв, так что границы слова представляют пробелы или любые символы, не относящиеся к перечисленным.

Следующий пример ищет слово class только когда это отдельное слово. Если оно содержится внутри другого слова, соответствия не находится:

>>> p = re.compile(r’\bclass\b’)
>>> print p.search(‘no class at all’)
<_sre.SRE_Match object at 0x…>
>>> print p.search(‘the declassified algorithm’)
None
>>> print p.search(‘one subclass is’)
None

Есть две тонкости, которые вы должны помнить при использовании этой специальной последовательности. Во-первых, это одно из худших столкновений между строковыми литералами Python и последовательностями регулярных выражений: в строковых литералах Python, \b это символ backspace, ASCII значение 8. Если не использовать «сырые» строки, Python будет конвертировать \b в backspace, и ваше регулярное выражение будет не таким, как задумано:

>>> p = re.compile(‘\bclass\b’)
>>> print p.search(‘no class at all’)
None
>>> print p.search(‘\b’ + ‘class’ + ‘\b’)
<_sre.SRE_Match object at 0x…>

Во-вторых, внутри класса символов нельзя использовать данное сочетание, потому как сочетание \b для совместимости со строковыми литералами Python представляет символ backspace.

\B

Противоположное предыдущему сочетание, соответствующая текущей позиции не на границе слова.

Группировка

Часто необходимо получить больше информации, чем просто узнать, находит ли РВ соответствие или нет. Регулярные выражения часто используются для разрезания строк написанием регулярных выражений, разделенных на несколько подгрупп, которые соответствуют различным компонентам запроса. Например, в стандарте RFC-822 в заголовке имеются различные поля, разделенные двоеточием:

From: [email protected]

User-Agent: Thunderbird 1.5.0.9 (X11/20061227)

MIME-Version: 1.0

To: [email protected]

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

Группы обозначаются метасимволами в виде круглых скобок '(', ')'. '(' и ')' имеют такой же смысл, как в математических выражениях; они группируют вместе выражения, содержащиеся в них, и вы можете повторять содержание группы повторяющими квалификаторами, такими как *, +, ? и {m, n}. Например, (ab)* будет соответствовать нулю или более повторений ab.

>>> p = re.compile(‘(ab)*’)
>>> print p.match(‘ababababab’).span()
(0, 10)

Группы, определяемые скобками, также захватывают начальные и конечные индексы совпадающего текста; это может быть получено передачей аргумента group(), start(), end() и span(). Группы нумеруются, начина с 0. Группа 0 имеется всегда, это само регулярное выражение целиком, так что методы MatchObject всегда содержат 0 как аргумент по умолчанию:

>>> p = re.compile(‘(a)b’)
>>> m = p.match(‘ab’)
>>> m.group()
‘ab’
>>> m.group(0)
‘ab’

Подгруппы нумеруются слева направо, от 1 и далее. Группы могут быть вложенными; для того чтобы определить число вложений, просто подсчитываем слева направо символы открывающей скобки:

>>> p = re.compile(‘(a(b)c)d’)
>>> m = p.match(‘abcd’)
>>> m.group(0)
‘abcd’
>>> m.group(1)
‘abc’
>>> m.group(2)
‘b’

group() может принять одновременно несколько номеров групп в одном запросе, и будет возвращен кортеж, содержащий значения для соответствующих групп:

>>> m.group(2,1,2)
(‘b’, ‘abc’, ‘b’)

Метод groups() возвращает кортеж строк для всех подгрупп, начиная с 1-ой:

>>> m.groups()
(‘abc’, ‘b’)

Обратные ссылки в шаблоне позволяют вам указать, что содержание ранее захваченной группы также должно быть найдено в текущей позиции строки. Например, \1 соответствует тому, что содержание группы 1 в точности повторяется в текущей позиции.

Например, следующее РВ обнаруживает в строке дважды подряд повторяющиеся слова:

>>> p = re.compile(r'(\b\w+)\s+\1′)
>>> p.search(‘Paris in the the spring’).group()
‘the the’

Обратные ссылки, такие, как эта, не часто бывают полезны для поиска по строке, но вы скоро узнаете, что они очень полезны при выполнении замены строки.

Группы с захватом содержимого и именованные группы

Регулярные выражения могут использовать множество групп, как для захвата необходимой подстроки, так и для группировки и структурирования самих РВ. В сложных регулярных выражениях становится трудно отслеживать номера групп. Есть две особенности, которые помогают справиться с этой проблемой. Обе из них используют общий синтаксис для расширения регулярных выражений, который мы поэтому и рассмотрим сначала.

В Perl 5 было добавлено несколько дополнительных функций для стандартных регулярных выражений, и модуль re поддерживает большинство из них. Было бы сложно выбрать новые односимвольные метасимволы или новые последовательности с бэкслешем, для того чтобы представить новые особенности так, чтобы регулярные выражения Perl без путаницы отличались от стандартных регулярных выражений. Если выбрать в качестве нового метасимвола, например, &, то старые регулярные выражения принимали бы его как обычный символ и нельзя было бы экранировать его \& или [&].

Решение, выбранное разработчиками Perl состояло в том, чтобы использовать в качестве расширения синтаксиса (?...). Знак вопроса после скобки в случае обычного РВ это синтаксическая ошибка, поскольку ? нечего повторять, так что это не приводит к каким-либо проблемам в совместимости. Символы сразу после ? показывают какое расширение используется, так (?=foo) это одно (положительное утверждение о предпросмотре), а (?:foo) это что-то другое (группа без захвата содержимого, включающая подвыражение foo).

К расширенному синтаксису Perl в Python добавляется собственное расширение. Если первый символ после знака вопроса P, то это означает что используется расширение, специфичное для Python. В настоящее время существуют два таких расширения: (?P<some_name>... )определяет именованную группу, а (?P=some_name) служит для нее обратной ссылкой. Если в будущих версиях Perl 5 добавятся аналогичные возможности, использующие другой синтаксис, модуль re будет изменен для поддержки нового синтаксиса, сохраняя при этом для совместимости Python-синтаксис.

Иногда вам нужно использовать группу для сбора части регулярного выражения, но вы не заинтересованы в извлечении содержимого группы. Вы можете сделать это, используя группу без захвата содержимого: (?:...), где вы можете заменить ... любым другим регулярным выражением:

>>> m = re.match(«([abc])+», «abc»)
>>> m.groups()
(‘c’,)
>>> m = re.match(«(?:[abc])+», «abc»)
>>> m.groups()
()

За исключением того, что вы не получаете содержимого того, с чем совпала группа, эти группы ведут себя также, как и обычные; вы можете в них поместить что угодно, повторить с помощью соответствующего символа, такого как *, и вставить их в другие группы (собирающие данные или нет).

Более важной особенностью являются именованные группы: вместо ссылки на них по номерам, на эти группы можно ссылаться по имени.

Синтаксис именованных групп это одно из специфичных Python-расширений: (?P<some_name>...). Именованные группы ведут себя в точности как обычные, но вдобавок к этому ассоциируются с каким-то именем. Методы MatchObject, которые использовались для обычных групп принимают как числа, ссылающиеся на номер группы, так и строки, содержащие имя необходимой группы. То есть именованные группы все также принимают и числа, так что вы можете получить информацию о группе двумя способами:

>>> p = re.compile(r'(?P<word>\b\w+\b)’)
>>> m = p.search( ‘(((( Lots of punctuation )))’ )
>>> m.group(‘word’)
‘Lots’
>>> m.group(1)
‘Lots’

Именованные группы удобны тем, что они позволяют использовать вместо цифр легко запоминающиеся имена. Вот пример регулярного выражения из модуля imaplib:

InternalDate = re.compile(r’INTERNALDATE «‘

        r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-‘

        r'(?P<year>[0-9][0-9][0-9][0-9])’

        r’ (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])’

        r’ (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])’

        r'»‘)

Синтаксис обратных ссылок в регулярном выражение типа (...)\1 ссылается на номер группы. Более естественно было бы использовать вместо номеров имена групп. Другое Python расширение: (?P=name) показывает, что содержимое названной группы снова должно быть сопоставлено в текущей позиции. Наше прежнее регулярное выражение для поиска дублирующихся слов, (\b\w+)\s+\1 может быть также записано как (?P<doble_word>\b\w+)\s+(?P=doble_word):

>>> p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)’)
>>> p.search(‘Paris in the the spring’).group()
‘the the’

Опережающие проверки

Проверки доступны в позитивной и негативной (ретроспективной) форме, и выглядят так:

(?=...)

Положительная проверка. Соответствует случаю, когда содержащееся выражение, представленное здесь как ..., соответствует текущей позиции. Но, после того как содержащееся выражение было опробовано, сравнивающий движок не продвигается далее; остаток шаблона сравнивается далее справа от того места, где начинается утверждение.

(?!...)

Отрицательное проверка соответствует случаю, когда содержащиеся выражение не соответствует текущей позиции строки.

Для конкретики, рассмотрим случай, в котором полезен предпросмотр. Рассмотрим простой шаблон для сравнения имени файла и разбиения его на части: само имя и расширение, отделенные друг от друга точкой.

Шаблон для такого сравнения довольно прост:

.*[.].*$

Отметим, что символ . требует специальных скобок, поскольку сама точка является метасимволом, что видно в этом же выражении. Также отметим заключительный $; он добавлен для того, чтобы обеспечить включение в расширение всего остатка строки.

Теперь, рассмотрим проблему немного шире; что, если вы хотите сравнить имена всех файлов, у которых расширение не bat? Несколько неверных попыток:

.*[.][^b].*$

Первая попытка состоит в том, чтобы исключить bat требованием, чтобы первый символ расширение был не b. Это неправильно, потому что шаблон также исключит foo.bar.

.*[.]([^b]..|.[^a].|..[^t])$

Выражение получится еще неряшливее, когда вы решите подправить первое решение отдельным заданием нужных символов: первый символ расширения должен быть не b; второй — не a; третий — не t. Это позволит включить foo.bar и отклонить autoexec.bat, но требует расширения из трех букв и не будет работать с двухсимвольными расширениями имен файлов, наприме sendmail.cf. Тогда нам придется снова усложнить шаблон, чтобы решить эту проблему:

.*[.]([^b].?.?|.[^a]?.?|..?[^t]?)$

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

Шаблон теперь действительно готов, его сложно читать и понимать. Еще хуже, если изменится проблема и вам нужно будет исключить и bat, и exe расширения, шаблон станет еще более сложным и запутанным.

Негативная опережающая проверка решает все эти затруднения:

.*[.](?!bat$).*$

Отрицательный предпросмотр означает: если выражение bat не соответствует данной позиции, сравнить остаток шаблона; если обнаружено соответствие bat$, то весь шаблон нам не подходит. Заключающий выражение знак $ нужен для того, чтобы было разрешено и такое выражение, как sample.batch.

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

.*[.](?!bat$|exe$).*$

Изменение строк

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





Метод/атрибутЦель
split()Разбить строку в список там, где есть совпадение РВ
sub()Найти все подстроки совпадений с РВ и заменить их другой строкой
subn()Делает то же, что и sub(), но возвращает новую строку и число замещений
Разбиение строк

Метод шаблона split() разбивает строку на части там, где есть совпадение РВ, возвращая список частей. Это похоже на строковый метод split(), но обеспечивает всеобщность в разделителях, по которым происходит разбиение; обычный split() обеспечивает разбиение только по whitespace-символам или фиксированной строке. Как и следовало ожидать, существует и модульная функция re.split().

.split(string[, maxsplit=0])

Разбивает строку по совпадениям с регулярным выражением. Если в РВ имеются захватывающие скобки, то их содержание также будет возвращаться как часть полученного списка. Если maxsplit не ноль, выполняется не более, чем maxsplit разбиений, остаток строки будет возвращен как последний элемент списка.

В следующем примере разделителем является любая последовательность небуквенно-цифровых символов.

>>> p = re.compile(r’\W+’)
>>> p.split(‘This is a test, short and sweet, of split().’)
[‘This’, ‘is’, ‘a’, ‘test’, ‘short’, ‘and’, ‘sweet’, ‘of’, ‘split’, »]
>>> p.split(‘This is a test, short and sweet, of split().’, 3)
[‘This’, ‘is’, ‘a’, ‘test, short and sweet, of split().’]

Иногда вы заинтересованы не только в том, какой текст был между разделителями, но также должны знать, какой разделитель использовался. Если в РВ есть захватывающие скобки, то и эти значения также возвращаются, как часть списка. Сравните:

>>> p = re.compile(r’\W+’)
>>> p2 = re.compile(r'(\W+)’)
>>> p.split(‘This… is a test.’)
[‘This’, ‘is’, ‘a’, ‘test’, »]
>>> p2.split(‘This… is a test.’)
[‘This’, ‘… ‘, ‘is’, ‘ ‘, ‘a’, ‘ ‘, ‘test’, ‘.’, »]

Функция модуля re.split() в качестве первого аргумента забирает РВ, а в остальном ведет себя также:

>>> re.split(‘[\W]+’, ‘Words, words, words.’)
[‘Words’, ‘words’, ‘words’, »]
>>> re.split(‘([\W]+)’, ‘Words, words, words.’)
[‘Words’, ‘, ‘, ‘words’, ‘, ‘, ‘words’, ‘.’, »]
>>> re.split(‘[\W]+’, ‘Words, words, words.’, 1)
[‘Words’, ‘words, words.’]

Поиск и замена

Другая распространенная задача — найти все совпадения с шаблоном и заменить их другой строкой. Метод sub() берет в качестве аргументов значение замещающей части (которая может быть как строкой, так и функцией) и строку, которая должна быть обработана.

.sub(replacement, string[, count=0])

Возвращает строку, получающуюся при замене. Если шаблон не найден строка возвращается неизменной.

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

Простой пример использования метода sub(). Названия цветов замещаются словом colour:

>>> p = re.compile( ‘(blue|white|red)’)
>>> p.sub(‘colour’, ‘blue socks and red shoes’)
‘colour socks and colour shoes’
>>> p.sub(‘colour’, ‘blue socks and red shoes’, count=1)
‘colour socks and red shoes’

Метод subn() делает то же самое, но возвращает кортеж, содержащий новую строку и число произведенных замен:

>>> p = re.compile( ‘(blue|white|red)’)
>>> p.subn( ‘colour’, ‘blue socks and red shoes’)
(‘colour socks and colour shoes’, 2)
>>> p.subn( ‘colour’, ‘no colours at all’)
(‘no colours at all’, 0)

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

>>> p = re.compile(‘x*’)
>>> p.sub(‘-‘, ‘abxd’)
‘-a-b-d-‘

Если заместителем служит строка, то в ней поддерживаются символы экранирования. Так, \n это одиночный символ новой строки, \r это возврат каретки и так далее. Обратные ссылки, такие как \6 замещаются подстрокой, совпавшей с соответствующей группой в РВ. Это позволяет вам включать части оригинального текста в результат замены строки.

Пример соответствует слову section в части строки, предшествующей части в фигурных скобках {, }, и заменяет section на subsection:

>>> p = re.compile(‘section{ ( [^}]* ) }’, re.VERBOSE)
>>> p.sub(r’subsection{\1}’,’section{First} section{second}’)
‘subsection{First} subsection{second}’

Также имеется возможность ссылаться на именованные группы. Для этого используется последовательность \g<...>, где в качестве ... может выступать номер или имя группы. \g<2> это эквивалент \2, но он не двусмыслен в таких заместителях, как \g<2>0. (\20 будет интерпретироваться как ссылка на группу 20, а не как вторую группу с последующим литералом ‘0’.) Следующие операции эквиваленты, но используют три различных способа:

>>> p = re.compile(‘section{ (?P<name> [^}]* ) }’, re.VERBOSE)
>>> p.sub(r’subsection{\1}’,’section{First}’)
‘subsection{First}’
>>> p.sub(r’subsection{\g<1>}’,’section{First}’)
‘subsection{First}’
>>> p.sub(r’subsection{\g<name>}’,’section{First}’)
‘subsection{First}’

Заместитель также может быть и функцией, дающем вам больше возможностей управления. Если это так, функция вызывается для каждого случая неперекрытия шаблона. При каждом вызове функции передается в качестве аргумента MatchObject.

В следующем примере функция замены переводит десятичные числа в шестнадцатеричные:

>>> def hexrepl( match ):

…     «Return the hex string for a decimal number»

…     value = int( match.group() )

…     return hex(value)


>>> p = re.compile(r’\d+’)
>>> p.sub(hexrepl, ‘Call 65490 for printing, 49152 for user code.’)
‘Call 0xffd2 for printing, 0xc000 for user code.’

Общие проблемы

Регулярные выражения это мощный инструмент для некоторых применений, но в некоторых отношениях их поведение не интуитивно, а иногда они не ведут себя так, как вы ожидаете от них. В этом разделе будет указано на некоторые из наиболее распространенных ошибок.

Использование строковых методов

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

Представим, нужно заменить одну фиксированную строку другой, например, заменить слово word словом deed. Здесь, конечно, подойдет функция re.sub(), но рассмотрим строковый метод replace(). Отметим, что replace() будет также заменять word внутри слов, меняя swordfish на sdeedfish, но и простецкое регулярное выражение будет делать это то же. (Чтобы избежать выполнения замещения на части слов, шаблон должен содержать \bword\b).

Другая распространенная задача это удаление одиночного символа из строки или замена его другим символом. Вы можете сделать это чем-то вроде re.sub('\n', ' ', S), но метод translate() справится с обеими задачами и сделает это быстрее, чем любое регулярное выражение.

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

match() в сравнении с search()

Функция match() ищет соответствие РВ в начале строки, тогда как search() ищет соответствие во всей строке. Важно иметь в виду это различие:

>>> print re.match(‘super’, ‘superstition’).span()
(0, 5)
>>> print re.match(‘super’, ‘insuperable’)
None
>>> print re.search(‘super’, ‘superstition’).span()
(0, 5)
>>> print re.search(‘super’, ‘insuperable’).span()
(2, 7)

У вас может возникнуть соблазн всегда пользоваться re.match(), просто добавляя перед вашим регулярным выражением .*. Сопротивляйтесь этому искушению, и вместо этого используйте re.search(). Компилятор регулярных выражений производит небольшой анализ РВ для того, чтобы ускорить процесс поиска соответствия. Один из видов такого анализа заключается в определении того, что должно быть первым символом совпадения, например, совпадение с шаблоном, начинающимся с Crow, должно начинаться с 'C'. Этот анализ приводит к тому, что движок быстро пробегает строку в поиске начального символа, и начинает полное сравнение только, если найден символ ‘C’.

Добавление .* сводит на нет эту оптимизацию, требуя сканирования до конца строки, а затем возвращения, чтобы сравнить остаток регулярного выражения. Используйте вместо этого re.search().

Жадный против нежадного

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

>>> s = ‘<html><head><title>Title</title>’
>>> len(s)
32
>>> print re.match(‘<.*>’, s).span()
(0, 32)
>>> print re.match(‘<.*>’, s).group()
<html><head><title>Title</title>

РВ сопоставляет '<' в первом теге — html, и .* забирает остаток строки. В итоге сопоставление простирается от открывающей склюки '<' тега html до закрывающей скобки ‘>' закрывающего тега /title, что, конечно, не есть то, чего мы хотели.

В таком случае, решение заключается в использовании нежадных определителей *?, +?, ?? или {m,n}?, которые сопоставляют так мало текста, как это возможно. В примере выше, будет выбран первый символ ‘>’ после ‘<‘, и только, если не получится, движок будет продолжать попытки найти символ ‘>’ на следующей позиции, в зависимости от того, как длинно имя тега. Это дает нужный результат:

>>> print re.match(‘<.*?>’, s).group()
<html>

(Заметим, что парсинг HTML или XML с использованием регулярных выражений является делом болезненным. Наспех сделанный шаблон может справиться с какими-то вещами, но рушится при изменении кода страницы. А хорошо продуманный шаблон может оказаться слишком сложным для того, чтобы пытаться его модифицировать. Для таких задач лучше использовать модули HTML или XML парсеров.)

Использование re.VERBOSE

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

Для таких РВ может быть полезным указание флага VERBOSE при компиляции регулярного выражения потому что это позволяет форматировать регулярное выражение более ясным образом.

Флаг VERBOSE имеет несколько особенностей. Пробелы в РВ, которые не находятся внутри класса символов игнорируется. Это означает, что выражения, такие как dog | cat эквивалентны менее читабельным беспробельным dog|cat, но [a b] будет по-прежнему соответствовать символам 'a', 'b' или пробелу. Кроме того, вы можете также помещать внутрь РВ комментарии, длящиеся с символа # до следующей строки. Форматирование будет более аккуратным при использовании тройных кавычек:

pat = re.compile(r»»»

 \s*                 # Skip leading whitespace

 (?P<header>[^:]+)   # Header name

 \s* :               # Whitespace, and a colon

 (?P<value>.*?)      # The header’s value — *? used to

                     # lose the following trailing whitespace

 \s*$                # Trailing whitespace to end-of-line

«»», re.VERBOSE)

Это гораздо проще читается, чем:

pat = re.compile(r»\s*(?P<header>[^:]+)\s*:(?P<value>.*?)\s*$»)

В заключение

Документация модуля re
Хабраблог «Регулярные выражения»
Regexp editor

Регулярные выражения (RegEx) — Документация TRegExpr 1.147

Вступление

Регулярные выражения — удобный способ описывать шаблоны текстов.

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

Ниже приведена исчерпывающая шпаргалка по регулярных выражениям всего на одной странице.

Символы

Простые совпадения

Серия символов соответствует этой серии символов во входной строке.

RegExНаходит
foobarfoobar

Непечатные символы (escape-коды)

Для представления непечатаемого символа в регулярном выражении используется \x с шестнадцатеричным кодом. Если код длиннее 2 цифр (более U+00FF), то он обрамляется в фигурные скобки.

RegExНаходит
\xABсимвол с 2-значным шестнадцатеричным кодом AB
\x{AB20}символ с 1-4 значным шестнадцатеричным кодом AB20
foo\x20barfoo bar (обратите внимание на пробел в середине)

Существует ряд предопределенных escape-кодов для непечатных символов, как в языке C:

RegExНаходит
\ttab (HT/TAB), тоже что \x09
\nсимвол новой строки (LF), то же что \x0a
\rвозврат каретки (CR), тоже что \x0d
\fform feed (FF), то же что \x0c
\aзвонок (BEL), тоже что \x07
\eescape (ESC), то же что \x1b
\cA\cZ

chr(0) по chr(25).

Например \cI соответствует табуляции.

Также поддерживаются буквы в нижнем регистре «a»…»z».

Эскейпинг

Для представления спецсимволов (.+*?|\()[]{}^$), перед ними надо поставить \. Чтобы вставить сам обратный слэш его надо удвоить.

Шпаргалка по регулярным выражениям — Exlab


Шпаргалка представляет собой общее руководство по шаблонам регулярных выражений без учета специфики какого-либо языка. Она представлена в виде таблицы, помещающейся на одном печатном листе формата A4. Создана под лицензией Creative Commons на базе шпаргалки, автором которой является Dave Child (подробнее).


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


Якоря

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

^[0-9]+

Здесь символ ^ обозначает начало строки. Без него шаблон соответствовал бы любой строке, содержащей цифру.


Символьные классы

Символьные классы в регулярных выражениях соответствуют сразу некоторому набору символов. Например, \d соответствует любой цифре от 0 до 9 включительно, \w соответствует буквам и цифрам, а \W — всем символам, кроме букв и цифр. Шаблон, идентифицирующий буквы, цифры и пробел, выглядит так:

\w\s


POSIX

POSIX — это относительно новое дополнение семейства регулярных выражений. Идея, как и в случае с символьными классами, заключается в использовании сокращений, представляющих некоторую группу символов.


Утверждения

Поначалу практически у всех возникают трудности с пониманием утверждений, однако познакомившись с ними ближе, вы будете использовать их довольно часто. Утверждения предоставляют способ сказать: «я хочу найти в этом документе каждое слово, включающее букву “q”, за которой не следует “werty”».

[^\s]*q(?!werty)[^\s]*

Приведенный выше код начинается с поиска любых символов, кроме пробела ([^\s]*), за которыми следует q. Затем парсер достигает «смотрящего вперед» утверждения. Это автоматически делает предшествующий элемент (символ, группу или символьный класс) условным — он будет соответствовать шаблону, только если утверждение верно. В нашем случае, утверждение является отрицательным (?!), т. е. оно будет верным, если то, что в нем ищется, не будет найдено.

Итак, парсер проверяет несколько следующих символов по предложенному шаблону (werty). Если они найдены, то утверждение ложно, а значит символ q будет «проигнорирован», т. е. не будет соответствовать шаблону. Если же werty не найдено, то утверждение верно, и с q все в порядке. Затем продолжается поиск любых символов, кроме пробела ([^\s]*).


Образцы шаблонов

В этой группе представлены образцы шаблонов. С их помощью вы можете увидеть, как можно использовать регулярные выражения в ежедневной практике. Однако заметьте, что они не обязательно будут работать в любом языке программирования, поскольку каждый из них обладает индивидуальными особенностями и различным уровнем поддержки регулярных выражений.


Кванторы

Кванторы позволяют определить часть шаблона, которая должна повторяться несколько раз подряд. Например, если вы хотите выяснить, содержит ли документ строку из от 10 до 20 (включительно) букв «a», то можно использовать этот шаблон:

a{10,20}

По умолчанию кванторы — «жадные». Поэтому квантор +, означающий «один или больше раз», будет соответствовать максимально возможному значению. Иногда это вызывает проблемы, и тогда вы можете сказать квантору перестать быть жадным (стать «ленивым»), используя специальный модификатор. Посмотрите на этот код:

".*"

Этот шаблон соответствует тексту, заключенному в двойные кавычки. Однако, ваша исходная строка может быть вроде этой:

<a href="helloworld.htm" title="Привет, Мир">Привет, Мир</a>

Приведенный выше шаблон найдет в этой строке вот такую подстроку:

"helloworld.htm" title="Привет, Мир"

Он оказался слишком жадным, захватив наибольший кусок текста, который смог.

".*?"

Этот шаблон также соответствует любым символам, заключенным в двойные кавычки. Но ленивая версия (обратите внимание на модификатор ?) ищет наименьшее из возможных вхождений, и поэтому найдет каждую подстроку в двойных кавычках по отдельности:

"helloworld.htm"
"Привет, Мир"


Специальные символы

Регулярные выражения используют некоторые символы для обозначения различных частей шаблона. Однако, возникает проблема, если вам нужно найти один из таких символов в строке, как обычный символ. Точка, к примеру, в регулярном выражении обозначает «любой символ, кроме переноса строки». Если вам нужно найти точку в строке, вы не можете просто использовать «.» в качестве шаблона — это приведет к нахождению практически всего. Итак, вам необходимо сообщить парсеру, что эта точка должна считаться обычной точкой, а не «любым символом». Это делается с помощью знака экранирования.

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

Шаблон для нахождения точки таков:

\.

Другие специальные символы в регулярных выражениях соответствуют необычным элементам в тексте. Переносы строки и табуляции, к примеру, могут быть набраны с клавиатуры, но вероятно собьют с толку языки программирования. Знак экранирования используется здесь для того, чтобы сообщить парсеру о необходимости считать следующий символ специальным, а не обычной буквой или цифрой.


Подстановка строк

Подстановка строк подробно описана в следующем параграфе «Группы и диапазоны», однако здесь следует упомянуть о существовании «пассивных» групп. Это группы, игнорируемые при подстановке, что очень полезно, если вы хотите использовать в шаблоне условие «или», но не хотите, чтобы эта группа принимала участие в подстановке.


Группы и диапазоны

Группы и диапазоны очень-очень полезны. Вероятно, проще будет начать с диапазонов. Они позволяют указать набор подходящих символов. Например, чтобы проверить, содержит ли строка шестнадцатеричные цифры (от 0 до 9 и от A до F), следует использовать такой диапазон:

[A-Fa-f0-9]

Чтобы проверить обратное, используйте отрицательный диапазон, который в нашем случае подходит под любой символ, кроме цифр от 0 до 9 и букв от A до F:

[^A-Fa-f0-9]

Группы наиболее часто применяются, когда в шаблоне необходимо условие «или»; когда нужно сослаться на часть шаблона из другой его части; а также при подстановке строк.

Использовать «или» очень просто: следующий шаблон ищет «ab» или «bc»:

(ab|bc)

Если в регулярном выражении необходимо сослаться на какую-то из предшествующих групп, следует использовать \n, где вместо n подставить номер нужной группы. Вам может понадобиться шаблон, соответствующий буквам «aaa» или «bbb», за которыми следует число, а затем те же три буквы. Такой шаблон реализуется с помощью групп:

(aaa|bbb)[0-9]+\1

Первая часть шаблона ищет «aaa» или «bbb», объединяя найденные буквы в группу. За этим следует поиск одной или более цифр ([0-9]+), и наконец \1. Последняя часть шаблона ссылается на первую группу и ищет то же самое. Она ищет совпадение с текстом, уже найденным первой частью шаблона, а не соответствующее ему. Таким образом, «aaa123bbb» не будет удовлетворять вышеприведенному шаблону, так как \1 будет искать «aaa» после числа.

Одним из наиболее полезных инструментов в регулярных выражениях является подстановка строк. При замене текста можно сослаться на найденную группу, используя $n. Скажем, вы хотите выделить в тексте все слова «wish» жирным начертанием. Для этого вам следует использовать функцию замены по регулярному выражению, которая может выглядеть так:

replace(pattern, replacement, subject)

Первым параметром будет примерно такой шаблон (возможно вам понадобятся несколько дополнительных символов для этой конкретной функции):

([^A-Za-z0-9])(wish)([^A-Za-z0-9])

Он найдет любые вхождения слова «wish» вместе с предыдущим и следующим символами, если только это не буквы или цифры. Тогда ваша подстановка может быть такой:

$1<b>$2</b>$3

Ею будет заменена вся найденная по шаблону строка. Мы начинаем замену с первого найденного символа (который не буква и не цифра), отмечая его $1. Без этого мы бы просто удалили этот символ из текста. То же касается конца подстановки ($3). В середину мы добавили HTML тег для жирного начертания (разумеется, вместо него вы можете использовать CSS или <strong>), выделив им вторую группу, найденную по шаблону ($2).


Модификаторы шаблонов

Модификаторы шаблонов используются в нескольких языках, в частности, в Perl. Они позволяют изменить работу парсера. Например, модификатор i заставляет парсер игнорировать регистры.

Регулярные выражения в Perl обрамляются одним и тем же символом в начале и в конце. Это может быть любой символ (чаще используется «/»), и выглядит все таким образом:

/pattern/

Модификаторы добавляются в конец этой строки, вот так:

/pattern/i


Мета-символы

Наконец, последняя часть таблицы содержит мета-символы. Это символы, имеющие специальное значение в регулярных выражениях. Так что если вы хотите использовать один из них как обычный символ, то его необходимо экранировать. Для проверки наличия скобки в тексте, используется такой шаблон:

\(


Рекомендуем также:

Основы регулярных выражений | Регулярные выражения

  Обновл. 17 Ноя 2019  | 

Регулярное выражение — это описание шаблона символов.

Вступление

Самый простой шаблон, который мы можем описать — это строка (или последовательность) символов. Например, я хочу выполнить поиск символов th (или, если говорить более конкретно, я ищу символ t, за которым непосредственно следует символ h) в следующем наборе символов:

Регулярное выражение: th
Пример: There is no theory of evolution. Only a list of animals Chuck Norris allows to live.

Вы можете быть удивлены, почему не было найдено совпадение в слове There. Дело в том, что это слово содержит заглавную букву T, а не строчную, как в искомом регулярном выражении. Мы знаем, что это один и тот же символ, просто в другой форме. Однако это не относится к регулярным выражениям. Регулярные выражения не определяют значения искомой последовательности. Всё, что они делают — это ищут точные совпадения с тем, что описывает шаблон.

Очень простое выражение, подобное этому, на самом деле ничем не отличается от поискового запроса, который вы можете выполнить в поисковой системе (Google/Яндекс) или в вашем любимом текстовом редакторе.

Метасимволы

Символ точки (.) является метасимволом. Метасимволы — это символы, которые имеют особое значение. Они помогают создавать более интересные шаблоны, чем просто набор конкретных символов. Практически всё, что мы будем рассматривать — будет метасимволами.

Точка (.) представляет собой любой символ. В следующем примере мы ищем символ b, за которым следует любой символ, а за ним символ g:

Регулярное выражение: b.g
Пример: The big bag of bits was bugged.

Важно отметить, что точка (.) соответствует только одному символу. Мы можем заставить её соответствовать более чем одному символу, используя множители, которые мы рассмотрим ниже. Кроме того, вы также можете искать несколько символов с помощью точки следующим образом:

Регулярное выражение: l..e
Пример: You can live like a king but make sure it isn't a lie.

В примере выше мы сопоставляем букву l, за которой следуют любые два символа, и за которыми следует e.

Диапазоны символов

Символ точки (.) позволяет нам установить соответствие любому символу. Но иногда нам необходимо быть более конкретными. В этом случае будут полезны диапазоны. Мы указываем диапазон символов, заключая их в квадратные скобки []:

Регулярное выражение: t[eo]d
Пример: When today is over Ted will have a tedious time tidying up.

В регулярном выражении выше мы ищем символ t, за которым следует либо символ e, либо o, и за которым следует символ d.

Количество символов, которые вы можете поместить в квадратные скобки, не ограничено. Вы можете разместить один символ, например, [у] (это было бы немного глупо, но, тем не менее, это не нарушает правила), или вы можете указать несколько символов, например, [Grf4s2 # lknx].

Короткая запись

Допустим, мы хотим найти наличие цифр от 1 до 8. Мы могли бы использовать для поиска следующий диапазон: [12345678], но есть вариант получше:

Регулярное выражение: [1-8]
Пример: Room Allocations: G4 G9 F2 h2 L0 K7 M9

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

Регулярное выражение: [1-49]
Пример: Room Allocations: G4 G9 F2 h2 L0 K7 M9

В регулярном выражении выше мы ищем цифры 1, 2, 3, 4 или 9.

Мы также можем объединить несколько наборов. В регулярном выражении ниже мы ищем 1, 2, 3, 4, 5, a, b, c, d, e, f, x:

Регулярное выражение: [1-5a-fx]
Пример: A random set of characters: y, w, a, r, f, 4, 9, 6, 3, p, x, t

Использование наборов символов иногда может привести к странному поведению. Например, вы можете использовать диапазон [a-f] и обнаружить, что ему соответствует символ D. Это связано с таблицами символов, которые использует система. В большинстве системах есть таблица символов, в которой сначала идут все строчные буквы, а затем заглавные (например, abcdef .... xyzABCD ...). Однако некоторые системы чередуют строчные и прописные буквы (например, aAbBcCdD ... yYzZ). Если вы столкнулись с каким-то странным поведением и, при этом, используете диапазоны, то это первое, что нужно проверять.

Поиск несоответствующих символов

Иногда нам может понадобиться найти символ, который не входит в диапазон искомых. Мы можем это сделать, поместив знак вставки (^) в начало диапазона:

Регулярное выражение: t[^eo]d
Пример: When today is over Ted will have a tedious time tidying up.

Совет: Любые символы, которые обычно имеют особое значение (метасимволы), теряют своё особое значение и становятся обычными символами, находясь внутри диапазона. Исключением является знак вставки (^), который приобретает новое значение — отрицание.

Мультипликаторы

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

   * — значение встречается 0 или более раз.

   + — значение встречается 1 или несколько раз.

   ? — значение встречается 0 или 1 раз.

   {5} — значение встречается 5 раз.

   {3,7} — значение встречается от 3 до 7 раз.

   {2,} — значение встречается не менее 2 раз.

Их действие распространяется на всё, что находится прямо перед ними. Это может быть обычный символ, например:

Регулярное выражение: lo*
Пример: Are you looking at the lock or the silk?

В примере выше мы ищем символ l, за которым следует символ o ноль или более раз. Вот почему l в слове silk также является совпадением (это l, за которым идёт символ o ноль раз).

Или это может быть метасимвол, например:

Регулярное выражение: l.*k
Пример: Are you looking at the lock or the silk?

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

Это то, что называется жадным сопоставлением. Это нормальное поведение — пытаться найти самую большую строку, которая может соответствовать шаблону. Мы можем изменить это поведение и сделать его не жадным, поместив вопросительный знак (?) после мультипликатора (что может показаться немного запутанным, поскольку вопросительный знак сам по себе является множителем):

Регулярное выражение: l.*?k
Пример: Are you looking at the lock or the silk?

Экранирование

Иногда нам может потребоваться найти один из символов, который является метасимволом. Для этого мы используем то, что называется экранированием. Поместив обратную косую черту (\) перед метасимволом, мы можем удалить его особое значение. Допустим, что нам нужно найти появление слова this, которое является последним словом в предложении.

Если бы мы сделали следующее:

Регулярное выражение: this.
Пример: Surely this regular expression should match this.

То нашли бы this. как в конце предложения, так и в начале, потому что точка в регулярном выражении обычно соответствует любому символу.

Решением будет следующее:

Регулярное выражение: this\.
Пример: Surely this regular expression should match this.

Совет: Когда метасимволы являются частью вашей искомой строки, то очень легко забыть об их экранировании. Если в результатах ваших регулярных выражений что-то не то, то внимательно обследуйте метасимволы, которые вы, возможно, забыли экранировать.

Механизм работы регулярных выражений

Теперь, когда у вас есть представление о том, что такое регулярные выражения, мы можем рассмотреть то, что происходит под капотом.

Итак, мы имеем указатель, который постепенно перемещается по строке поиска. Как только он сталкивается с символом, который соответствует началу регулярного выражения, он останавливается. Теперь запускается второй указатель, который перемещается вперёд от первого указателя, символ за символом, проверяя с каждым шагом, сохраняется ли соответствие шаблону или нет. Если мы доберёмся до конца шаблона и сохраняется соответствие, то мы нашли совпадение. Если на любом шаге происходит сбой, второй указатель отбрасывается, а основной указатель продолжает движение по строке.

Заключение

Резюмируем то, что мы узнали:

   . — соответствует любому символу;

   [ ] — совпадает с символом из диапазона, содержащегося в квадратных скобках;

   [^] — соответствует символам, которые не входят в диапазон, содержащийся в квадратных скобках;

   * — совпадение 0 или более раз с указанным элементом;

   + — совпадение 1 или более раз с указанным элементом;

   ? — совпадение 0 или 1 раз с указанным элементом;

   {n} — точное совпадение n раз указанного элемента;

   {n, m} — точное совпадение от n до m раз указанного элемента;

   {n, } — точное совпадение n или более раз указанного элемента;

   \ — экранирование или отмена специального значения указанного символа.

Оценить статью:

Загрузка…

Поделиться в социальных сетях:

Регулярное выражение

— исключить определенные числа из диапазона чисел с помощью регулярного выражения

Переполнение стека

  1. Около
  2. Продукты

  3. Для команд
  1. Переполнение стека
    Общественные вопросы и ответы

  2. Переполнение стека для команд
    Где разработчики и технологи делятся частными знаниями с коллегами

  3. Вакансии
    Программирование и связанные с ним технические возможности карьерного роста

  4. Талант
    Нанимайте технических специалистов и создавайте свой бренд работодателя

  5. Реклама
    Обратитесь к разработчикам и технологам со всего мира

  6. О компании

.Регулярное выражение

— Регулярные выражения POSIX: исключение слова из выражения?

Переполнение стека

  1. Около
  2. Продукты

  3. Для команд
  1. Переполнение стека
    Общественные вопросы и ответы

  2. Переполнение стека для команд
    Где разработчики и технологи делятся частными знаниями с коллегами

  3. Вакансии
    Программирование и связанные с ним технические возможности карьерного роста

  4. Талант
    Нанимайте технических специалистов и создавайте свой бренд работодателя

  5. Реклама
    Обратитесь к разработчикам и технологам со всего мира

  6. О компании

Загрузка…

  1. Авторизоваться
    зарегистрироваться

  2. текущий

.

regex — исключить символ из регулярного выражения PHP

Переполнение стека

  1. Около
  2. Продукты

  3. Для команд
  1. Переполнение стека
    Общественные вопросы и ответы

  2. Переполнение стека для команд
    Где разработчики и технологи делятся частными знаниями с коллегами

  3. Вакансии
    Программирование и связанные с ним технические возможности карьерного роста

  4. Талант
    Нанимайте технических специалистов и создавайте свой бренд работодателя

  5. Реклама
    Обратитесь к разработчикам и технологам со всего мира

  6. О компании

.

Исключить нежелательные символы — тестер / отладчик регулярных выражений

Классы символов
. любой символ, кроме новой строки
\ ш \ д \ с слово, цифра, пробел
\ W \ D \ S не слово, цифра, пробел
[abc] любой из a, b или c
[^ abc] не a, b или c
[а-г] символ между a и g
Анкеры
^ abc $ начало / конец строки
\ б граница слова
Экранированные символы
\.\ * \\ экранированных специальных символов
\ t \ n \ r табуляция, перевод строки, возврат каретки
\ u00A9 Unicode сброшен ©
Группы и поиск
(abc) группа захвата
\ 1 обратная ссылка на группу № 1
(?: Abc) группа без захвата
(? = Abc) положительный прогноз
(?! Abc) негативный прогноз
Квантификаторы и чередование
а * а + а? 0 или более, 1 или более, 0 или 1
а {5} а {2,} ровно пять, два или больше
а {1,3} между одним и тремя
а +? а {2,}? совпадений как можно меньше
ab | cd соответствует ab или cd

.