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

Практические регулярные выражения появились в ранних версиях Unix и использовались в редакторе ed. Обычно регулярные выражения ассоциируются с утилитой grep (Global search for Regular Expressions and Print lines).

Содержание

Зачем они нужны?

ОболочкаShell практически не имеет никаких встроенных средств обработки текста, кроме самых примитивных; эта задача перекладывается на внешние программы. Регулярные выражения (regular expressions) — мощный и эффективный инструмент обработки текста, совместно с возможностями оболочки позволяет конструировать простые и компактные инструменты.

Где используются

Регулярные выражения используются в таких программах, как sed (ЯзыкSed, потоковый редактор), grep (поиск текста по шаблону), awk (ЯзыкAWK, язык для обработки текстов и отчётов) и многих других. Существует две разновидности (стиля) регулярных выражений; они отличны друг от друга, и в то же время имело место взаимопроникновение. В настоящее время POSIX специфицирует BRE (basic regular expressions — базовые регулярные выражения) и ERE (extended regular expressions — расширенные регулярные выражения). Синтаксис BRE поддерживается большинством утилит Unix.

Шаблоны на основе регулярных выражений

Для демонстрации примеров регулярных выражений будет использоваться утилита grep. Это удобный способ интерактивной отладки и проверки регулярных выражений.

 $ echo 'Billy and Jimmy' | grep 'Jimmy' 
 Billy and Jimmy
 $

Если регулярное выражение совпадает со строкой, то grep печатает её на терминал. Определить было ли совпадение со строкой можно и по коду возврата grep так:

 $ echo 'Mikhail Bulgakov' | grep 'Mikhail' >/dev/null && echo 'OK' 
 OK
 $

Предыдущий пример подавляет вывод grep средствами оболочки (перенаправление в /dev/null), в то же время POSIX предлагает опцию -q для этой цели.

 $ echo 'Leo Tolstoy' | grep -q 'Tolstoy' && echo 'OK' 
 OK
 $

Привязка выражений к началу и концу строки

Регулярное выражение дает совпадение со строкой в том случае, если искомый образец содержится в любом месте строки. Символы «ˆ» и «$», используемые для привязки совпадения к началу и концу строки, называют якорями. Например, регулярное выражение apple совпадает со строкой myapples, но ˆapple$ даст совпадение только со строкой apple. В то же время регулярное выражение ˆapple даст совпадение с applesaregood, а apple$ совпадёт со строкой redapple.

 $ echo myapples | grep 'apple' 
 myapples
 $ echo apple | grep '^apple$' 
 apple
 $ echo badapple | grep '^apple$' 
 $ echo applesaregood | grep '^apple' 
 applesaregood
 $ echo redapple | grep 'apple$' 
 redapple
 $

В регулярных выражениях символ «ˆ» имеет два значения. Встретившись в самом начале выражения он работает как якорь, привязывая образец к началу строки; выражение ˆ[a-z] совпадает с любой строкой, начинающейся со строчной буквы латинского алфавита. Следуя после открывающей квадратной скобки ([) символ «ˆ» инвертирует группу символов; выражение [ˆa-z] совпадает с любой строкой, не содержащей строчных букв латинского алфавита.

 $ echo aqua | grep '[a-z]' 
 aqua
 $ echo aqua | grep '[^a-z]' 
 $ echo 1qua | grep '[^a-z]' 
 1qua
 $ echo 'a3ua' | grep '^[a-z]' 
 a3ua
 $ echo '9qua' | grep '^[a-z]' 
 $

Оператор «*», атомы и подвыражения

В регулярных выражениях в отличие от шаблонов оболочки оператор «*» не даёт совпадения сам по себе, а изменяет поведение предыдущего символа. Регулярное выражение cat* даст совпадение со строками ca, cat и catxyz.

 $ echo ca | grep 'cat*' 
 ca
 $ echo cat | grep 'cat*' 
 cat
 $ echo catxyz | grep 'cat*' 
 catxyz
 $

Точка (.) совпадает с любым одиночным символом, а «*» изменяет его поведение так, что выражение даёт совпадение с любой последовательностью символов, в том числе и с пустой строкой. Оператор «*» действует, если быть точным, не на предыдущий символ, а на элементарную конструкцию называемую атомом (atom). Любой одиночный символ является атомом. Атом можно получить из любого выражения, заключив его в \(…\). Например, регулярное выражение ma\(ma\)* совпадает со строками ma, mama, mamama, но не совпадает с mam.

 $ echo 'ma' | grep 'ma\(ma\)*' 
 ma
 $ echo 'mama' | grep 'ma\(ma\)*' 
 mama
 $ echo 'mamama' | grep 'ma\(ma\)*' 
 mamama
 $ echo mam | grep 'ma\(ma\)*' 
 $

Любое выражение, заключённое в \(…\), называется подвыражением (subexpression). Подвыражения могут быть вложенными на любую глубину.

Обратные ссылки

В регулярном выражении может присутствовать обратная ссылка (back-reference) на строку (возможно, пустую) совпавшую с подвыражением. Такую ссылку вставляют на порядковый номер подвыражения (от 1 до 9). Нумерация подвыражений начинается с единицы (\1, \2 и т. д.), а подсчёт ведётся по открывающей \(. Например, регулярное выражение \([0-9]\)\1 совпадёт со строками 00, 33 и т. д. — с любой строкой, содержащей две одинаковых цифры.

 $ echo 00 | grep '\([0-9]\)\1' 
 00
 $ echo 33 | grep '\([0-9]\)\1' 
 33
 $ echo 59 | grep '\([0-9]\)\1' 
 $

На вложенные подвыражения тоже можно ссылаться. Так регулярное выражение \([0-9]\([a-z]\)\)\1\2 совпадает со строками 0a0aa, 1b1bb, но со строкой 1a0aa совпадения уже нет.

 $ echo 0a0aa | grep '\([0-9]\([a-z]\)\)\1\2' 
 0a0aa
 $ echo 1b1bb | grep '\([0-9]\([a-z]\)\)\1\2' 
 1b1bb 
 $ echo 1a0aa | grep '\([0-9]\([a-z]\)\)\1\2' 
 $

Следует добавить, что обратные ссылки тоже являются атомами. Кроме оператора «*» в регулярных выражениях существует возможность уточнить количество и/или диапазон совпадений атома операторами \{x\} — точно x совпадений, \{x,\} — минимум x совпадений и \{x,y\} — от x совпадений до y совпадений включительно), где 0 ≤ xy.

Например, примитивную проверку IP-адреса можно осуществить следующим образом:

 $ echo 192.255.255.255 | grep '^\([0-9]\{3\}\.\)\{3\}[0-9]\{3\}$' 
 192.255.255.255
 $ echo 1.2.3 | grep '^\([0-9]\{3\}\.\)\{3\}[0-9]\{3\}$' 
 $

Расширенные регулярные выражения (ERE)

Расширенные регулярные выражения обычно ассоциируются с egrep (или grep -E). В отличие от базовых регулярных выражений, в ERE для группировки и в операторах повтора не используются обратные слэши, по этой причине они читаются и записываются проще. Например, предыдущий пример с проверкой IP-адреса записывается в ERE так:

 $ echo 192.255.255.255 | grep -E '^([0-9]{3}\.){3}[0-9]{3}$' 
 192.255.255.255
 $

Ещё одним отличием от BRE является наличие оператора «ИЛИ» (читается как «OR»).

Регулярное выражение orange|apple совпадает и со строкой apple и со строкой orange.

 $ echo apple | grep -E 'orange|apple' 
 apple
 $ echo orange | grep -E 'orange|apple' 
 orange
 $

Оператор «|» имеет низкий приоритет, поэтому регулярное выражение a|bc совпадает с a или bc, но не с ac и bc.

 $ echo a | grep -E 'a|bc' 
 a
 $ echo bc | grep -E 'a|bc' 
 bc
 $ echo ac | grep -E 'a|bc 
 $ echo bc | grep -E 'a|bc' 
 $

Чтобы получить совпадение с ac и bc нужно поставить скобки: (a|b)c.

 $ echo ac | grep -E '(a|b)c' 
 ac
 $ echo bc | grep -E '(a|b)c' 
 bc
 $

Обратные ссылки в ERE отсутствуют (в некоторых реализациях они есть как расширение). Считается, что обратные ссылки могут приводить к весьма неэффективным по производительности конструкциям; они избыточны в синтаксисе регулярных выражений и в большинстве случаев можно обойтись без них. Для удобства в ERE добавлены ещё два оператора повторения «?» и «+». Оператор «?» равнозначен {0,1}, а оператор «+» равнозначен {1,}. Заметим, что многие реализации BRE тоже «понимают» \+, \? и оператор \|.

Краткое руководство

c
Любой неспециальный символ c совпадает сам с собой.
\c
Отменить действие специального символа c.
ˆ
Привязать выражение к началу строки.
$
Привязать выражение к концу строки.
.
Совпадает с любым одиночным символом.
[…]
Совпадает с любым символом из ...; диапазоны задают в виде a-z.
[ˆ…]
Совпадает с любым cимволом, кроме указанного в ...; можно задавать диапазон.
\n
Строка с которой совпало n-ное подвыражение \(...\), где 0 ≤ n ≤ 9.
\(…\)
Превращает … в подвыражение.
r*
Совпадает с выражением r ноль или больше раз; совпадает с пустой строкой.
r\{x\}
Совпадает с выражением r точно x раз.
r\{x,\}
Совпадает с выражением r от x раз и больше.
r\{x,y\}
Совпадает с выражением r от x раз до y раз (включительно).
r1 r2
Конкатенация (объединение) выражений r1 и r2.

Литература

1 The Open Group Base Specifications Issue 7 [Электронный ресурс]
Regular Expressions / The IEEE and The Open Group. — IEEE Std 1003.1, 2013 Edition. — 2013. — Режим доступа: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html, свободный.


КатегорияЯзыкиПрограммирования