Руководство C# | Регулярные выражения
82C# — Руководство по C# — Регулярные выражения
Регулярные выражения — это часть небольшой технологической области, невероятно широко используемой в огромном диапазоне программ. Регулярные выражения можно представить себе как мини-язык программирования, имеющий одно специфическое назначение: находить подстроки в больших строковых выражениях.
Это не новая технология, изначально она появилась в среде UNIX и обычно используется в языке программирования Perl. Разработчики из Microsoft перенесли ее в Windows, где до недавнего времени эта технология применялась в основном со сценарными языками. Однако теперь регулярные выражения поддерживаются множеством классов .NET из пространства имен System.Text.RegularExpressions. Случаи применения регулярных выражений можно встретить во многих частях среды .NET Framework. В частности, вы найдете их в серверных элементах управления проверкой ASP.NET.
Введение в регулярные выражения
Язык регулярных выражений предназначен специально для обработки строк. Он включает два средства:
Набор управляющих кодов для идентификации специфических типов символов
Система для группирования частей подстрок и промежуточных результатов таких действий
С помощью регулярных выражений можно выполнять достаточно сложные и высокоуровневые действия над строками:
Идентифицировать (и возможно, помечать к удалению) все повторяющиеся слова в строке
Сделать заглавными первые буквы всех слов
Преобразовать первые буквы всех слов длиннее трех символов в заглавные
Обеспечить правильную капитализацию предложений
Выделить различные элементы в URI (например, имея http://www.professorweb.ru, выделить протокол, имя компьютера, имя файла и т.д.)
Главным преимуществом регулярных выражений является использование метасимволов — специальные символы, задающие команды, а также управляющие последовательности, которые работают подобно управляющим последовательностям C#. Это символы, предваренные знаком обратного слеша (\) и имеющие специальное назначение.
В следующей таблице специальные метасимволы регулярных выражений C# сгруппированы по смыслу:
Символ | Значение | Пример | Соответствует |
---|---|---|---|
Классы символов | |||
[…] | Любой из символов, указанных в скобках | [a-z] | В исходной строке может быть любой символ английского алфавита в нижнем регистре |
[^…] | Любой из символов, не указанных в скобках | [^0-9] | В исходной строке может быть любой символ кроме цифр |
. | Любой символ, кроме перевода строки или другого разделителя Unicode-строки | ||
\w | Любой текстовый символ, не являющийся пробелом, символом табуляции и т.п. | ||
\W | Любой символ, не являющийся текстовым символом | ||
\s | Любой пробельный символ из набора Unicode | ||
\S | Любой непробельный символ из набора Unicode. Обратите внимание, что символы \w и \S — это не одно и то же | ||
\d | Любые ASCII-цифры. Эквивалентно [0-9] | ||
\D | Любой символ, отличный от ASCII-цифр. Эквивалентно [^0-9] | ||
Символы повторения | |||
{n,m} | Соответствует предшествующему шаблону, повторенному не менее n и не более m раз | s{2,4} | «Press», «ssl», «progressss» |
{n,} | Соответствует предшествующему шаблону, повторенному n или более раз | s{1,} | «ssl» |
{n} | Соответствует в точности n экземплярам предшествующего шаблона | s{2} | «Press», «ssl», но не «progressss» |
? | Соответствует нулю или одному экземпляру предшествующего шаблона; предшествующий шаблон является необязательным | Эквивалентно {0,1} | |
+ | Соответствует одному или более экземплярам предшествующего шаблона | Эквивалентно {1,} | |
* | Соответствует нулю или более экземплярам предшествующего шаблона | Эквивалентно {0,} | |
Символы регулярных выражений выбора | |||
| | Соответствует либо подвыражению слева, либо подвыражению справа (аналог логической операции ИЛИ). | ||
(…) | Группировка. Группирует элементы в единое целое, которое может использоваться с символами *, +, ?, | и т.п. Также запоминает символы, соответствующие этой группе для использования в последующих ссылках. | ||
(?:…) | Только группировка. Группирует элементы в единое целое, но не запоминает символы, соответствующие этой группе. | ||
Якорные символы регулярных выражений | |||
^ | Соответствует началу строкового выражения или началу строки при многострочном поиске. | ^Hello | «Hello, world», но не «Ok, Hello world» |
professorweb.ru
6 пунктов, которые помогут легко разобраться с regexp
Давно хотели изучить regexp? Это небольшое руководство поможет разобраться с ними в 6 этапов, а обилие примеров позволит закрепить материал.
Regexp представляет собой группу символов или знаков, которая используется для поиска определенного текстового шаблона.
Регулярное выражение – это шаблон, который сравнивается с предметной строкой слева направо. Словосочетание “regular expression” применяется не так широко, вместо него обычно употребляют “regex” и “regexp”. Регулярное выражение используется для замены текста внутри строки, проверки формы, извлечения подстроки из строки на основе соответствия шаблона и т. д.
Предположим, вы создаете приложение и хотите определить правила, согласно которым пользователи будут выбирать себе имя. Например, мы хотим, чтобы оно содержало буквы, цифры, нижнее подчеркивание и дефисы. Также нам бы хотелось ограничить количество символов в имени пользователя, чтобы оно не выглядело уродливым. Поэтому для проверки будем использовать следующее регулярное выражение:
Это выражение принимает строки john_doe
, jo-hn_doe
и john12_as
. Однако имя пользователя Jo не будет соответствовать этому выражению, потому что оно содержит прописную букву, а также является слишком коротким.
Регулярное выражение — это всего лишь шаблон из символов, который мы используем для выполнения поиска в тексте. Например, регулярное выражение the
означает букву t
, за которой следует буква h
, за которой следует буква e
.
"the" => The fat cat sat on the mat.
Регулярное выражение 123
соответствует строке 123
. Регулярное выражение сопоставляется входной строке путем сравнения каждого символа в regexp с каждым символом входной строки. Регулярное выражение и входная строка сравниваются посимвольно. Обычно regex чувствительны к регистру, поэтому The
не соответствует строке the
.
"The" => The fat cat sat on the mat.
Тестировать выражение
Метасимволы служат строительными блоками regexp. Они не являются независимыми и обычно интерпретируются каким-либо образом. Некоторые метасимволы имеют особое значение, а потому помещаются в квадратные скобки. Метасимволы:
Метасимволы | Описание |
---|---|
. | Любой единичный символ, исключая новую строку. |
[ ] | Поиск набора символов, помещенных в скобки. |
[^ ] | Negated character class. Matches any character that is not contained between the square brackets |
* | 0 или больше повторений предшествующего символа. |
+ | 1 или больше повторений предшествующего символа. |
? | Делает предшествующий символ опциональным. |
{n,m} | Возвращает как минимум «n», но не более «m» повторений предшествующего символа. |
(xyz) | Находит группу символа в строго заданном порядке. |
| | Разделяет допустимые варианты. |
\ | Исключает следующий символ. Позволяет искать служебные символы [ ] ( ) { } . * + ? ^ $ \ | |
^ | Находит начало введенной строки. |
$ | Находит конец введенной строки. |
2.1 Точка
.
— это простейший пример метасимвола. Метасимвол .
соответствует любому единичному символу. Например, регулярное выражение .ar
означает: любой символ, за которым следует буква a
, за которой следует буква r
.
«.ar» => The car parked in the garage.
Тестировать выражение
2.2 Интервал символов
Интервал или набор символов также называют символьным классом. Для его обозначения используются квадратные скобки. Чтобы указать диапазон символов внутри класса, необходимо поставить знак тире. Порядок ряда символов в наборе неважен. Так, например, регулярное выражение [Tt]he
означает: T
или t
, за которым следует буква h
, за которой следует буква e
.
«[Tt]he » => The car parked in the garage.
Тестировать выражение
Стоит отметить, что точка, помещенная в квадратные скобки, означает именно точку, а ничто другое. Таким образом регулярное выражение ar[.]
означает строчный символ a
, за которым следует буква r
, за которой следует точка.
.
«ar [.]» => A garage is a good place to park a car.
Тестировать выражение
2.2.1 Отрицание набора символов
Обычно символ ^
представляет начало строки, но когда он внутри квадратных скобок, все символы, которые находятся после него, исключаются из шаблона. Например, выражение [^c]ar
поможет отыскать все символы кроме c
, за которыми следуют а
и r
.
"[^c]ar" => The car parked in the garage.
Тестировать выражение
2.3 Повторения
Следующие мета-символы +
,*
или ?
используются для того, чтобы обозначить допустимое количество повторения подшаблона. Их роль зависит от конкретного случая.
2.3.1 Звездочка
Этот символ поможет найти одно или более копий какого-либо символа. Регулярное выражение a*
означает 0 или более повторений символа a. Но если этот символ появится после набора или класса символов, тогда будут найдены повторения всего сета. Например, выражение [a-z]*
означает любое количество этих символов в строке.
"[a-z]*" => The car parked in the garage #21.
Тестировать выражение
Также символ может быть использован вместе с метасимволом .
для подбора строки из любых символов .*
.
Еще звездочку можно использовать со знаком пробела \s
, чтобы подобрать строку из пробелов. Например, выражение \s*cat\s
будет означать 0 или более пробелов, за которыми следует символ с
, за ним а
и t
, а за ними снова 0 либо больше пробелов.
"\s*cat\s*" => The fat cat sat on the concatenation.
Тестировать выражение
2.3.2 Плюс
+
соответствует одному или нескольким повторениям предыдущего символа. Например, регулярное выражение c.+t
означает: строчная буква c
, за которой следует хотя бы один символ, за которым следует строчный символ t
. Необходимо уточнить, что буква t
должна быть последней t в предложении.
"c.+t" => The fat cat sat on the mat.
Тестировать выражение
2.3.3. Вопросительный знак
В regexp метасимвол ?
делает предшествующий символ необязательным. Этот символ соответствует полному отсутствию или же одному экземпляру предыдущего символа. Например, регулярное выражение [T]?he
означает: необязательно заглавную букву T
, за которой следует строчный символ h
, за которым следует строчный символ e
."[T]he" => The car is parked in the garage.
Тестировать выражение
"[T]?he" => The car is parked in the garage.
Тестировать выражение
2.4 Скобки
Скобки в regexp, которые также называются квантификаторами, используются для указания допустимого количества повторов символа или группы символов. Например, регулярное выражение [0-9]{2,3}
означает, что допустимое количество цифр должно быть не менее двух цифр, но не более 3 (символы в диапазоне от 0 до 9).
"[0-9]{2,3}" => The number was 9.9997 but we rounded it off to 10.0.
Тестировать выражение
Мы можем убрать второе число. Например, выражение [0-9]{2,}
означает 2 или более цифр. Если мы также уберем запятую, то тогда выражение [0-9]{3}
будет находить только лишь 3 цифры, ни меньше и ни больше.
"[0-9]{2,}" => The number was 9.9997 but we rounded it off to 10.0.
Тестировать выражение
"[0-9]{3}" => The number was 9.9997 but rounded it off to 10.0.
Тестировать выражение
2.5 Символьная группа
Группа символов — это группа подшаблонов, которая записывается внутри скобок (...)
. Как было упомянуто раньше, если в регулярном выражении поместить квантификатор после символа, он повторит предыдущий символ. Но если мы поставим квантификатор после группы символов, он просто повторит всю группу. Например, регулярное выражение (ab)*
соответствует нулю или более повторениям символа «ab». Мы также можем использовать |
(c|g|p)ar
означает: символ нижнего регистра c
, g
или p
, за которым следует символ a
, за которым следует символ r
."(c|g|p)ar" => The car is parked in the garage.
Тестировать выражение
2.6 Перечисление
В regexp вертикальная полоса |
используется для определения перечисления. Перечисление — это что-то вроде условия между несколькими выражениями. Можно подумать, что набор символов и перечисление работают одинаково, но это совсем не так, между ними существует огромная разница. Перечисление работает на уровне выражений, а набор символов на уровне знаков. Например, регулярное выражение (T|t)he|car
означает: T
или t
, сопровождаемая строчным символом h
, сопровождаемый строчным символом
или строчным символом c
, а затем a
и r
.
"(T|t)he|car" => The car is parked in the garage.
Тестировать выражение
2.7 Исключение специального символа
Обратная косая черта \
используется в regexp, чтобы избежать символа, который следует за ней. Это позволяет нам указывать символ в качестве символа соответствия, включая зарезервированные { } [ ] / \ + * . $ ^ | ?
. Чтобы использовать специальный символ в качестве подходящего, перед ним нужно поставить \
.
Например, регулярное выражение .
используется для нахождения любого единичного символа. Регулярное выражение (f|c|m)at\.?
означает строчную букву f
, c
или m
, а затем a
, за ней t
с последующим дополнительным символом .
.
"(f|c|m)at\.?" => The fat cat sat on the mat.
Тестировать выражение
2.8 Анкеры — Привязки
В regexp мы используем привязки, чтобы проверить, является ли соответствующий символ первым или последним символом входной строки. Привязка бывает двух типов: первый — это ^
, который проверяет является ли соответствующий символ первым введенным, а второй — знак доллара, который проверяет, является ли соответствующий символ последним символом введенной строки.
2.8.1. Caret
Символ ^
используется в regexp, чтобы проверить, является ли соответствующий символ первым символом в введенной строке. Если мы применяем следующее регулярное выражение ^a
(проверяем является ли a первым символом) для введенной строки abc
, то оно будет равно a
. Но если мы применим регулярное выражение ^b
к той же строке, то оно ничего не вернет, потому что во входной строке abc
символ «b» не является первым. Давайте посмотрим на другое регулярное выражение ^(T|t)he
, которое означает: T
или t
— это символ начала входной строки, за которым следует строчный символ h
, а затем e
.
"(T|t)he" => The car is parked in the garage.
Тестировать выражение
"^(T|t)he" => The car is parked in the garage.
Тестировать выражение
2.8.2 Доллар
Знак доллара используется для проверки, является ли символ в выражении последним в введенной строке. Например (at\.)$
означает строчную а
, за которой следует t
, за которой следует a .
, которые должны заканчивать строку.
"(at\.)" => The fat cat. sat. on the mat.
Тестировать выражение
"(at\.)$" => The fat cat. sat. on the mat.
Тестировать выражение
Regexp позволяет использовать сокращения для некоторых наборов символов, что делает работу с ними более комфортной. Таким образом, здесь используются следующие сокращения:
Сокращение | Описание |
---|---|
. | Любой символ, кроме новой строки |
\w | Соответствует буквенно-цифровым символам:[a-zA-Z0-9_] |
\W | Соответствует не буквенно-цифровым символам:[^\w] |
\d | Соответствует цифрам: [0-9] |
\D | Соответсвует нецифровым знакам: [^\d] |
\s | Соответствует знаку пробела: [\t\n\f\r\p{Z}] |
\S | Соответствует символам без пробела: [^\s] |
Lookbehind и lookahead (также называемые lookaround) — это определенные типы non-capturing групп (Они используются для поиска, но сами в него не входят). Lookaheads используются, когда у нас есть условие, что этому шаблону предшествует или следует другой шаблон. Например, мы хотим получить все числа, которым предшествует символ $
из входной строки $4.44 and $10.88
. Мы будем использовать регулярное выражение (?<=\$)[0-9\.]*
, которое означает: получить все числа, содержащие .
и которым предшествует символ $
. Ниже приведены lookarounds, что используются в регулярных выражениях:
Символ | Описание |
---|---|
?= | Положительный Lookahead |
?! | Отрицательный Lookahead |
?<= | Положительный Lookbehind |
?<! | Отрицательный Lookbehind |
4.1 Положительный Lookahead
Положительный lookahead означает, что эта часть выражения должна следовать за впереди идущим выражением. Возвращаемое значение содержит текст, который совпадает с первой частью выражения. Чтобы определить позитивный lookahead, используют скобки. Внутри них размещают знак вопроса и знак равенства: (?=...)
. Само же выражение пишется после =
. Например, выражение (T|t)he(?=\sfat)
— это T
в верхнем или нижнем регистре, за которым следует h
и e
. В скобках мы определяем позитивный lookahead, который говорит движку регулярного выражения искать The
или the
, за которыми следует fat
.
"(T|t)he(?=\sfat)" => The fat cat sat on the mat.
Тестировать выражение
4.2 Отрицательный Lookahead
Негативный lookahead используется, когда нам нужно получить все совпадения в строке, за которой не следует определенный шаблон. Негативный lookahead определяется так же, как и позитивный, с той лишь разницей, что вместо знака равенства мы используем знак отрицания !
. Таким образом, наше выражение приобретает следующий вид: (?!...)
. Теперь рассмотрим (T|t)he(?!\sfat)
, что означает: получить все The
или the
в введенной строке, за которыми не следует слово fat
, предшествующее знаку пробела.
"(T|t)he(?!\sfat)" => The fat cat sat on the mat.
Тестировать выражение
4.3 Положительный Lookbehind
Положительный lookbehind используется для получения всех совпадений, которым предшествует определенный шаблон. Положительный lookbehind обозначается так: (?<=...)
. Например, регулярное выражение (?<=(T|t)he\s)(fat|mat)
означает получить все fat
или mat
из строки ввода, которые идут после слова The
или the
.
"(? The fat cat sat on the mat.
Тестировать выражение
4.4 Отрицательный Lookbehind
Отрицательный lookbehind используется для получения всех совпадений, которым не предшествует определенный шаблон. Отрицательный lookbehind обозначается выражением (?<!...)
. Например, регулярное выражение (?<!(T|t)he\s)(cat)
означает: получить все cat слова из строки ввода, которые не идут после The или the.
"(? The cat sat on cat.
Тестировать выражение
Флаги также часто называют модификаторами, так как они могут изменять вывод regexp. Флаги, приведенные ниже являются неотъемлемой частью и могут быть использованы в любом порядке или сочетании regexp.
Флаг | Описание |
---|---|
i | Нечувствительность к регистру: делает выражение нечувствительным к регистру. |
g | Глобальный поиск: поиск шаблона во всей строке ввода. |
m | Многострочность: анкер метасимвола работает в каждой строке. |
5.1 Нечувствительные к регистру
Модификатор i
используется для поиска совпадений, нечувствительных к регистру. Например, выражение /The/gi
означает прописную букву T
, за которой следуют h
и e
. И в самом конце выражения стоит i
, благодаря которому можно проигнорировать регистр. g
применяется для того, чтобы найти шаблон во всей введенной строке."The" => The fat cat sat on the mat.
Тестировать выражение
"/The/gi" => The fat cat sat on the mat.
Тестировать выражение
5.2 Глобальный поиск
Модификатор используется для выполнения глобального поиска шаблона(поиск будет продолжен после первого совпадения). Например, регулярное выражение /.(at)/g
означает любой символ, кроме новой строки, за которым следует строчный символ a
, а
затем t
. Поскольку мы использовали флаг g
в конце регулярного выражения, теперь он найдет все совпадения в вводимой строке, а не только в первой (что является стандартом).
"/.(at)/" => The fat cat sat on the mat.
Тестировать выражение
"/.(at)/g" => The fat cat sat on the mat.
Тестировать выражение
5.3 Многострочный поиск
Модификатор m
нужен для выполнения многострочного поиска. Как было сказано раннее, привязки (^, $)
используются для проверки, является ли шаблон началом или концом строки. Но если мы хотим, чтобы привязки работали в каждой строке, нужно использовать флаг m
. Например, регулярное выражение /at(.)?$/gm
означает: строчный символ a
, за которым следует t
и что угодно, только не новая строка. А благодаря флагу m
этот механизм регулярных выражений соответствует шаблону в конце каждой строки строки.
"/.at(.)?$/" => The fat
cat sat
on the mat.
Тестировать выражение
"/.at(.)?$/gm" => The fat
cat sat
on the mat.
Тестировать выражение
По умолчанию регулярные выражения выполняются благодаря «жадным» квантификаторам, им соответсвует максимально длинная строка из всех возможных.
"/(.*at)/" => The fat cat sat on the mat.
Тестировать выражение
Чтобы получить «ленивое» выражение, нужно использовать ?
. Так будет получена максимально короткая строка.
"/(.*?at)/" => The fat cat sat on the mat.
Тестировать выражение
Источник
proglib.io
Шпаргалка по регулярным выражениям
Квантификаторы
Аналог | Пример | Описание | |
---|---|---|---|
? | {0,1} | a? | одно или ноль вхождений «а» |
+ | {1,} | a+ | одно или более вхождений «а» |
* | {0,} | a* | ноль или более вхождений «а» |
Модификаторы
Символ «минус» (-) меред модификатором (за исключением U) создаёт его отрицание.
Описание | |
---|---|
g | глобальный поиск (обрабатываются все совпадения с шаблоном поиска) |
i | игнорировать регистр |
m | многострочный поиск. Поясню: по умолчанию текст это одна строка, с модификатором есть отдельные строки, а значит ^ — начало строки в тексте, $ — конец строки в тексте. |
s | текст воспринимается как одна строка, спец символ «точка» (.) будет вкючать и перевод строки |
u | используется кодировка UTF-8 |
U | инвертировать жадность |
x | игнорировать все неэкранированные пробельные и перечисленные в классе символы |
Спецсимволы
Аналог | Описание | |
---|---|---|
() | подмаска, вложенное выражение | |
[] | групповой символ | |
{a,b} | количество вхождений от «a» до «b» | |
| | логическое «или», в случае с односимвольными альтернативами используйте [] | |
\ | экранирование спец символа | |
. | любой сивол, кроме перевода строки | |
\d | [0-9] | десятичная цифра |
\D | [^\d] | любой символ, кроме десятичной цифры |
\f | конец (разрыв) страницы | |
\n | перевод строки | |
\pL | буква в кодировке UTF-8 при использовании модификатора u | |
\r | возврат каретки | |
\s | [ \t\v\r\n\f] | пробельный символ |
\S | [^\s] | любой символ, кроме промельного |
\t | табуляция | |
\w | [0-9a-z_] | любая цифра, буква или знак подчеркивания |
\W | [^\w] | любой символ, кроме цифры, буквы или знака подчеркивания |
\v | вертикальная табуляция |
Спецсимволы внутри символьного класса
Пример | Описание | |
---|---|---|
^ | [^da] | отрицание, любой символ кроме «d» или «a» |
— | [a-z] | интервал, любой симво от «a» до «z» |
Позиция внутри строки
Пример | Соответствие | Описание | |
---|---|---|---|
^ | ^a | aaa aaa | начало строки |
$ | a$ | aaa aaa | конец строки |
\A | \Aa | aaa aaa aaa aaa | начало текста |
\z | a\z | aaa aaa aaa aaa | конец текста |
\b | a\b \ba | aaa aaa aaa aaa | граница слова, утверждение: предыдущий символ словесный, а следующий — нет, либо наоборот |
\B | \Ba\B | aaa aaa | отсутствие границы слова |
\G | \Ga | aaa aaa | Предыдущий успешный поиск, поиск остановился на 4-й позиции — там, где не нашлось a |
Якоря
Якоря в регулярных выражениях указывают на начало или конец чего-либо. Например, строки или слова. Они представлены определенными символами. К примеру, шаблон, соответствующий строке, начинающейся с цифры, должен иметь следующий вид:
^[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" "Привет, Мир"
Экранирование в регулярных выражениях
Регулярные выражения используют некоторые символы для обозначения различных частей шаблона. Однако, возникает
проблема, если вам нужно найти один из таких символов в строке, как обычный символ. Точка, к примеру, в регулярном
выражении обозначает «любой символ, кроме переноса строки». Если вам нужно найти точку в строке, вы не можете просто
использовать «.
» в качестве шаблона — это приведет к нахождению практически всего. Итак, вам
необходимо сообщить парсеру, что эта точка должна считаться обычной точкой, а не «любым символом». Это делается с
помощью знака экранирования.
Знак экранирования, предшествующий символу вроде точки, заставляет парсер игнорировать его функцию и считать обычным символом. Есть несколько символов, требующих такого экранирования в большинстве шаблонов и языков. Вы можете найти их в правом нижнем углу шпаргалки («Мета-символы»).
Шаблон для нахождения точки таков:
\.
Другие специальные символы в регулярных выражениях соответствуют необычным элементам в тексте. Переносы строки и табуляции, к примеру, могут быть набраны с клавиатуры, но вероятно собьют с толку языки программирования. Знак экранирования используется здесь для того, чтобы сообщить парсеру о необходимости считать следующий символ специальным, а не обычной буквой или цифрой.
Спецсимволы экранирования в регулярных выражениях
Выражение | Соответствие |
---|---|
\ | не соответствует ничему, только экранирует следующий за ним символ. Это нужно, если вы хотите ввести метасимволы !$()*+.?[\]^{|} в качестве их буквальных значений. |
\Q | не соответствует ничему, только экранирует все символы вплоть до \E |
\E | не соответствует ничему, только прекращает экранирование, начатое \Q |
Подстановка строк
Подстановка строк подробно описана в следующем параграфе «Группы и диапазоны», однако здесь следует упомянуть о существовании «пассивных» групп. Это группы, игнорируемые при подстановке, что очень полезно, если вы хотите использовать в шаблоне условие «или», но не хотите, чтобы эта группа принимала участие в подстановке.
Группы и диапазоны
Группы и диапазоны очень-очень полезны. Вероятно, проще будет начать с диапазонов. Они позволяют указать набор подходящих символов. Например, чтобы проверить, содержит ли строка шестнадцатеричные цифры (от 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
Мета-символы
Наконец, последняя часть таблицы содержит мета-символы. Это символы, имеющие специальное значение в регулярных выражениях. Так что если вы хотите использовать один из них как обычный символ, то его необходимо экранировать. Для проверки наличия скобки в тексте, используется такой шаблон:
\(
Шпаргалка представляет собой общее руководство по шаблонам регулярных выражений без учета специфики какого-либо языка. Она представлена в виде таблицы, помещающейся на одном печатном листе формата A4. Создана под лицензией Creative Commons на базе шпаргалки, автором которой является Dave Child. Скачать в PDF, PNG.
- Регулярные выражения
website-lab.ru
Практическое введение в регулярные выражения для новичков
Легкое и веселое введение в теорию регулярных выражений от веб-разработчика Джоша Хоукинса — Regex, охватывающее все основные моменты, которые нужно знать новичку.
Вы когда-нибудь работали со строками? Да-да, с теми самыми «массивами символов», которые мы все знаем и любим. Если вы программировали на чем-нибудь, кроме чистого С, с уверенностью можно предположить, что работали, причем не раз. Но что, если вы имеете дело со множеством строк? Или со строками, которые сгенерированы не вашей программой? Например, вы считываете электронное письмо, парсите аргументы командной строки или читаете инструкции, написанные человеком, и вам нужен более структурированный метод работы со всем этим.
Безусловно, вы можете перебирать каждое слово или символ во всех строках. И, вероятно, подобный код будет довольно прост для понимания. Но в масштабных приложениях это может быть излишне громоздким и слишком ресурсозатратным.
Не забираясь в дебри глубокой информатики, давайте дадим определение регулярному выражению.
- Регулярные выражения — это правила, описывающие некоторый формальный язык.
- Регулярные выражения — разновидность формального языка, которая может быть обработана конечным автоматом.
Существует множество других определений регулярных выражений, так что, если сказанного выше для счастья вам недостаточно, можете потратить пару минут на гуглинг.
Сейчас, возможно, мы подошли к месту, где пора испугаться (если вы до сих пор этого не сделали). Мы рассмотрим различия между тем, что вкладывается в понятие регулярных выражений языками программирования, а что — фундаментальной информатикой.
- Регулярные выражения с точки зрения информатики — правила, объясняющие формальный язык.
- Регулярные выражения с точки зрения языков программирования — грамматика, выражающая, в большей степени, некоторый контекстно-зависимый язык.
Контекстно-зависимые языки ощутимо сложнее и мощнее, так что с этого момента условимся называть регулярные выражения в терминах языков программирования „regex“, дабы подчеркнуть их обособленность от формальных языков в целом.
Регулярные выражения описываются с помощью двух слэшей (//) и соответствуют строкам, подходящим под шаблон, заключенный между ними. Например, /Hi/ соответствует „Hi“, так что мы можем проверить соответствие некоторой строки этому шаблону.
Символы в регулярных выражениях сопоставляются в том порядке, в котором вводятся. Так /Hello world/ отвечает строке „Hello world“.
Можно упростить поиск произвольных слов, добавив немного regex-магии: \w соответствует любому «слову», составленному только из букв. По такому же принципу идентифицируются числа: \d.
Превосходно, теперь мы можем сравнивать строки или проверять их соответствие некоторому паттерну. Что дальше? Могут ли регулярные выражения выполнять еще какие-нибудь функции?
Будьте уверены! Скажем, мы написали IRC чат бота, который реагирует, если кто-то напишет „Josh“. Наш бот сканирует каждое сообщение, пока не дождется совпадения. Тогда бот отвечает: „Woah, I hope you aren’t talking bad about my pal Josh!“ («О, надеюсь, вы не будете говорить плохо о моем приятеле Джоше!»). Потому что с Джошами дружат только роботы.
…
Для сравнения строк наш бот использует шаблон /Josh/. В один прекрасный момент некто по имени Eli обронит: „Eli: Josh, do you really need that much caffeine?“ («Эли: Джош, тебе действительно необходимо такое количество кофеина?»). Наш бот навострит ушки, обнаружит совпадение, выдаст свой неожиданный ответ, чем достаточно напугает Эли. Миссия выполнена! Или нет?
Что, если бы наш бот был более умным? Что, если бы он, например, обращался к говорящему по имени? Что-нибудь вроде „Woah, I hope you aren’t bad-mouthing my buddy Josh, Eli.“ («О, надеюсь, ты не будешь злословить о моем приятеле Джоше, Эли?»).
0 и более
Мы можем сделать это… Но для начала нужно уяснить пару моментов. Первый — квантификаторы (для повторяющихся символов). Можно использовать *, чтобы обозначить 0 или несколько символов после. Например, /a*/ может соответствовать „aaaaaaa“, а также „“. Да, вы не ослышались: оно будет отвечать пустой строке.
* служит для обозначения чего-то необязательного, так как символ, которому она соответствует, существовать вовсе не обязан. Но он может. И не раз (теоретически, бесчисленное множество раз).
1 и более
Для обозначения одного и более символов используется +. Он эффективно работает по тому же принципу, что и *, за исключением того, что существование хотя бы одного символа более не является опциональным: должен присутствовать по крайней мере один.
Таким образом, мы можем задать шаблоном /J+osh/ строки „Josh“ или „Jjjjjjjjjosh“, но не „osh“.
Прекрасно, мы уже во многом развязали себе руки. Возможно, сейчас кто-то вопит «Джоооооош», если уже достаточно разозлился…
Но что, если он злится настолько сильно, что даже пару раз ударил лицом по клавиатуре? Как нам обозначить «ааавыопшадлорвпт», не зная заранее, насколько меток его нос?
Метасимволы позволяют задавать абсолютно ЧТО УГОДНО. Их синтаксис — . . (Да, точка. Просто точка.). Бьемся об заклад, вы часто пользуетесь ею, так что не стесняйтесь обозначать ей конец предложения.
Можно задать „Joooafhuaisggsh“ выражением /Jo+.*sh/, комбинируя полученные ранее знания о повторяющихся символах и метасимволах. Если быть точными, данное выражение соответствует одной „J“, одному или более „o“, нулю или нескольким метасимволам, а также одной „s“ и одной „h“. Эти пять блоков подводят нас к тому, что мы называем…
Группы символов — это символьные блоки, в которых важна последовательность составляющих. Они рассматриваются как единое целое. Используя * или +, вы фактически задаете последовательность повторяющейся группы символов, а не только лишь последнего символа.
Это полезно понять и как отдельную технику, но большую функциональность она обретает в сочетании с повторяющимися символами. Группы символов задаются с помощью круглых скобок (да-да, этих ребят).
Допустим, мы хотим повторять „Jos“, но не „h“. что-то вроде „JosJosJosJosJosh“. Это можно сделать выражением /(Jos)+h/. Просто, не правда ли?
Но наконец… Возвращаясь к нашему первому примеру, как нам получить имя Эли в нашем IRC чате из отправленного ею сообщения?
Группы символов могут также служить для запоминания подстрок. Для этого обычно делают что-то вроде \1, чтобы определить первую заданную группу.
Например, /(.+) \1/ — особый случай. Здесь мы видим набор случайных символов, повторяющийся один или более раз, пробел после него, а затем повторение точно такого же набора еще раз. Так что такое выражение будет соответствовать „abc abc“, но не „abc def“, даже если „def“ сам по себе отвечает
Запоминание совпадений — очень мощная штука, и она, вероятно, сводится к самой полезной функции регулярных выражений.
Фух… Наконец-то можно вернуться к примеру с IRC чат ботом. Давайте применим наши знания на практике.
Если мы хотим выцепить имя отправителя сообщения, когда он пишет „Josh“, наше выражение будет выглядеть примерно так: /(\w+): .*Josh.*/, и мы сможем сохранить результат в переменной в нашем языке программирования для ответа.
Давайте рассмотрим наше регулярное выражение. Здесь одна или более букв, следующих за «:», 0 или более символов, „Josh“ и снова 0 или более символов.
Заметка: /.*word.*/ — простой способ задать строку, содержащую „word“, причем другие символы в ней могут присутствовать, а могут и нет.
На Python это будет выглядеть следующим образом:import re
pattern = re.compile(ur'(\w+): .*Josh.*') # Our regex
string = u"Eli: Josh go move your laundry" # Our string
matches = re.match(pattern, string) # Test the string
who = matches.group(1) # Get who said the message
print(who) # "Eli"
Заметьте, что мы использовали .group(1) точно так же, как \1. В этом нет ничего нового, за исключением использования регулярных выражений в Питоне.
До этого момента мы предполагали, что искомые подстроки могут находиться в любом месте строки. К примеру, /(Jos)+h/ соответствует любой строке, которая содержит „Jos-повторяющееся-h“ в произвольном месте.
А что, если нам необходимо, чтобы строка начиналась с этого шаблона? Это можно обозначить как /^(Jos)+h/, где ^ соответствует началу строки. Аналогично, $ обозначает конец строки.
Теперь, если мы хотим задать строку, содержащую только „Jos-повторяющееся-h“, то напишем /^(Jos)+h$/.
Представьте, что вы пишете регулярное выражение для рецепта бутерброда. Вы не знаете, предпочитает заказчик белый хлеб или черный, но выбрать все равно придется только один. Как добавить возможность выбора в regex? С помощью перечислений!
Они позволяют задавать наборы возможных значений для группы символов. Это выглядит следующим образом: (white|wheat). В контексте нашего примера с бутербродом, будет принят один из вариантов — либо „white“, либо „wheat“.
Для обозначения перечислений несколько по-другому используют [квадратные скобки]. Вместо всей строки, здесь вариантом является каждый ее символ. Это может быть полезно для сложных регулярных выражений, так как вы можете заменить один символ более сложным набором.
Мы говорили о regex с /двумя слэшами/, верно? Мы знаем, что находится между ними, но что должно быть снаружи?
Неожиданный поворот: ничего!
…слева. Правая сторона, напротив, может содержать множество, множество всего полезного. Даже стыдно, что мы так долго не сказали об этом ни слова!
Модификаторы задают правила, по которым применяются регулярные выражения.
Вот список основных модификаторов (с Regex101.com):
Модификатор | Название | Описание |
g | global | Все совпадения |
m | multi-line | ^ и $ соответствуют началу и концу каждой строки |
i | insensitive | Регистронезависимое сравнение |
x | extended | Пробелы и текст после # игнорируются |
X | extra | \ с произвольной буквой, не имеющей особого значения, возвращает ошибку |
s | single line | Игнорирует символы новой строки |
u | unicode | Строки-шаблоны обрабатываются как UTF-16 |
U | ungreedy | По умолчанию в regex используется «ленивая квантификация». Модификатор U делает квантификацию «жадной» |
A | anchored | Шаблон форсируется к ^ |
J | duplicate | Разрешает дублирующиеся имена субпаттерннов |
Для наглядности, все предыдущие примеры были регистрозависимыми. Это значит, что если заменить хотя бы одну строчную букву на заглавную или наоборот, строка перестанет удовлетворять шаблону. Но можно сделать его регистронезависимым с помощью модификатора i.
Предположим, Эли взбесилась настолько, что начала бомбить чат сообщениями с БуквАМи рАЗных РегИсТРОв. Это нас не страшит, потому что i уже здесь! Мы можем легко задать гневливое выражение „I hAate LiVing witH JOSH!!!“ паттерном /i ha+te living with josh!+/i. Теперь наши regex стали легче читаемы, а также намного более мощны и полезны. Замечательно!
Вы можете самостоятельно поиграть с разными модификаторами. На мой взгляд, в целом вам больше всего пригодится igm.
Надеюсь, эта статья позволила по-другому взглянуть на работу со строками и сделать ее более разумной. Мы лишь слегка прошлись по поверхности, но вы уже научились использовать regex для решения некоторых задач.
Существует множество символов и их сочетаний, используемых в регулярных выражениях. Обычно вы будете натыкаться на них во время изучения Stack Overflow, но о значении некоторых можно догадаться и из предыдущих примеров (например, \n — символ перехода на новую строку). База заложена, но выучить предстоит еще очень многое.
Найти полный список сочетаний символов, а также проверить свои знания можно здесь.
Если это показалось для вас проще простого, попробуйте regex-кроссворды. Они действительно заставят вас попотеть.
Эта статья — перевод гайда Джоша Хоукинса. Джош — страстный веб-разработчик из Алабамы. Он начал программировать в возрасте девяти лет, сосредоточившись на видеоиграх, десктопных и некоторых мобильных приложениях. Однако, во время стажировки в 2015, Джош открыл для себя веб-разработку и ворвался в мир опенсорса, связанного с этой областью.
proglib.io
Регулярные выражения, основы для работы в Linux
Доброго времени, гости!
В сегодняшней статье хочу коснуться такой огромной темы как Регулярные выражения. Думаю всем известно, что тема регексов (так регулярные выражения называются на сленге) — необъятна в объеме одного поста. Посему постараюсь кратко, но как можно понятней собрать в кучу свои мысли и донести их до Вас в своем блоге.
Начну с того, что существует несколько разновидностей регулярных выражений:
1. Традиционные регулярные выражения (они же основные, базовые и basic regular expressions (BRE))
- синтаксис данных выражений определен, как устаревший, но тем не менее до сих пор широко распространен и используется многими утилитами UNIX
- Основные регулярные выражения включают в себя следующие метасимволы (об их значениях ниже):
.
[
]
[^
]
^
$
*
\{
\}
— первоначальный вариант для{
} (в расширенных)
\(
\)
— первоначальный вариант для(
)
(в расширенных)
\n
, где n — номер от 1 до 9
- Особенности использования данных метасимволов:
- Звёздочка должна следовать после выражения, соответствующего единичному символу. Пример:
[xyz]*
. - Выражение
\(блок\)*
следует считать неправильным. В некоторых случаях оно соответствует нулю или более повторений строкиблок
. В других оно соответствует строкеблок*
. - Внутри символьного класса специальные значения символов, в основном, игнорируются. Особые случаи:
- Чтобы добавить символ
^
в набор, его следует поместить туда не первым. - Чтобы добавить символ
-
в набор, его следует поместить туда первым или последним. Например:- шаблон DNS-имени, куда могут входить буквы, цифры, минус и точка-разделитель:
[-0-9a-zA-Z.]
; - любой символ, кроме минуса и цифры:
[^-0-9]
.
- шаблон DNS-имени, куда могут входить буквы, цифры, минус и точка-разделитель:
- Чтобы добавить символ
[
или]
в набор, его следует поместить туда первым. Например:[][ab]
соответствует]
,[
,a
илиb
.
- Звёздочка должна следовать после выражения, соответствующего единичному символу. Пример:
2. Расширенные регулярные выражения (они же extended regular expressions (ERE))
- Синтаксис данных выражений аналогичен синтаксису основных выражений, за исключением:
- Отменено использование обратной косой черты для метасимволов
{ }
и( )
. - Обратная косая черта перед метасимволом отменяет его специальное значение.
- Отвергнута теоретически нерегулярная конструкция
\n
. - Добавлены метасимволы
+
,?
,|
.
- Отменено использование обратной косой черты для метасимволов
3. Регулярные выражения, совместимые с Perl (они же Perl-compatible regular expressions (PCRE))
- имеют более богатый и в то же время предсказуемый синтаксис, чем даже POSIX ERE, поэтому часто используется приложениями.
Далее немного поговорим о синтаксисе регулярных выражений.
Регулярные выражения состоят из шаблонов, вернее сказать задают шаблон поиска. Шаблон состоит из правил поиска, которые составляются из символов и метасимволов.
Правила поиска определяются следующими операциями:
Перечисление |
Вертикальная черта (|) разделяет допустимые варианты, можно сказать — логическое ИЛИ. Например, «gray|grey» соответствует gray или grey.
Группировка или объединение ( )
Круглые скобки используются для определения области действия и приоритета операторов. Например, «gray|grey» и «gr(a|e)y» являются разными образцами, но они оба описывают множество, содержащее gray и grey.
Квантификация {} ? * +
Квантификатор после символа или группы определяет, сколько раз предшествующее выражение может встречаться.
{m,n}
общее выражение, повторений может быть от m до n включительно.
{m,}
общее выражение, m и более повторений.
{,n}
общее выражение, не более n повторений.
{n}
ровно n повторений.
?
Знак вопроса означает 0 или 1 раз, то же самое, что и {0,1}. Например, «colou?r» соответствует и color, и colour.
*
Звёздочка означает 0, 1 или любое число раз ({0,}). Например, «go*gle» соответствует ggle, gogle, google и др.
+
Плюс означает хотя бы 1 раз ({1,}). Например, «go+gle» соответствует gogle, google и т. д. (но не ggle).
Конкретный синтаксис данных регулярных выражений зависит от реализации. (то есть в базовых регулярных выражениях символы { и } — экранируются обратным слешем)
Метасимволы, говоря простым языком — это символы, которые не соответствуют своему реальному значению, то есть символ . (точка) — это не точка, а любой один символ, и т.п. прошу ознакомиться с метасимволами и их значениями:
. | соответствует одному любому символу |
[что-то] | Соответствует любому единичномусимволу из числа заключённых в скобки. При этом:Символ «-» интерпретируется буквально только в том случае, если он расположен непосредственно после открывающей или перед закрывающей скобкой: [abc-] или [-abc]. В противном случае, он обозначает интервал символов.Например, [abc] соответствует «a», «b» или «c». [a-z] соответствует буквам нижнего регистра латинского алфавита. Эти обозначения могут и сочетаться: [abcq-z] соответствует a, b, c, q, r, s, t, u, v, w, x, y, z.Чтобы установить соответствие символам «[» или «]», достаточно, чтобы закрывающая скобка была первым символом после открывающей: [][ab] соответствует «]», «[», «a» или «b».Если значение в квадратных скобах предварено символом ^, то значение выражения соответствует единичному символу из числа тех, которых нет в скобках. Например, [^abc] соответствует любому символу, кроме «a», «b» или «c». [^a-z] соответствует любому символу, кроме символов нижнего регистра в латинском алфавите. |
^ | Соответствует началу текста (или началу любой строки, если режим построчный). |
$ | Соответствует концу текста (или концу любой строки, если режим построчный). |
\(\) или ( ) | Объявляет «отмеченное подвыражение» (сгруппированное выражение), которое может быть использовано позже (см. следующий элемент: \n). «Отмеченное подвыражение» также является «блоком». В отличие от других операторов, этот (в традиционном синтаксисе) требует бэкслеша, в расширенном и Perl символ \ — не нужен. |
\n | Где n — это цифра от 1 до 9; соответствует n-му отмеченному подвыражению (например (abcd)\0, то есть символы abcd отмечены нулем). Эта конструкция теоретически нерегулярна, она не была принята в расширенном синтаксисе регулярных выражений. |
* |
!!! Выражение, заключённое в «\(» и «\)» и сопровождаемое «*», следует считать неправильным. В некоторых случаях, оно соответствует нулю или более вхождений строки, которая была заключена в скобки. В других, оно соответствует выражению, заключённому в скобки, учитывая символ «*». |
\{x,y\} | Соответствует последнему (предстоящему) блоку, встречающемуся не менее x и не более y раз. Например, «a\{3,5\}» соответствует «aaa», «aaaa» или «aaaaa». В отличие от других операторов, этот (в традиционном синтаксисе) требует бэкслеша. |
.* | Обозначение любого количества любых символов между двумя частями регулярного выражения. |
Метасимволы нам помогают использовать различные соответствия. Но как же представить метасимвол обычным символом, то есть символ [ (квадратная скобка) значением квадратной скобки? Просто:
- необходимо предварить (экранировать) метасимвол (. * + \ ? [ ] { } ) обратным слешем. Например \. или \[
Для упрощения задания некоторых наборов символов, их объединили в т.н.классы и категории символов. POSIX стандартизовал объявление некоторых классов и категорий символов, как показано в следующей таблице:
POSIX класс | аналогично | обозначение |
[:upper:] | [A-Z] | символы верхнего регистра |
[:lower:] | [a-z] | символы нижнего регистра |
[:alpha:] | [A-Za-z] | символы верхнего и нижнего регистра |
[:alnum:] | [A-Za-z0-9] | цифры, символы верхнего и нижнего регистра |
[:digit:] | [0-9] | цифры |
[:xdigit:] | [0-9A-Fa-f] | шестнадцатеричные цифры |
[:punct:] | [.,!?:…] | знаки пунктуации |
[:blank:] | [ \t] | пробел и TAB |
[:space:] | [ \t\n\r\f\v] | символы пропуска |
[:cntrl:] | символы управления | |
[:graph:] | [^ \t\n\r\f\v] | символы печати |
[:print:] | [^\t\n\r\f\v] | символы печати и символы пропуска |
В regex есть такое понятие как:
Жадность regex
Постараюсь описать как можно понятней. Допустим, мы хотим найти все HTML теги в каком-то тексте. Локализовав задачу, мы хотим найти значения заключенные между < и >, вместе с этими самыми скобками. Но мы знаем, что теги имеют разную длину и самих тегов, как минимум штук 50. Перечислять их все , заключив в метасимволы [] — задача слишком трудоемкая. Но мы знаем, что у нас есть выражение .* (точка звездочка), характеризующее любое число любых символов в строке. С помощью данного выражения мы попытаемся найти в тексте (<p>Итак, <strong>Как создать RAID уровня 10/50 на контроллере LSI MegaRAID (актуально и для: Intel SRCU42x, Intel SRCS16):</strong><span></span></p>) все значения между < и >. В результате, этому выражению будет соответствовать ВСЯ строка. почему, потому что регекс — ЖАДЕН и старается захватить ЛЮБОЕ ВСЕ количество символов между < и >, соответственно вся строка, начиная <p>Итак,… и заканчивая …</span></p> будет принадлежать данному правилу!
Надеюсь, на примере понятно что такое жадность. Чтобы избавиться от данной жадности, можно пойти по следующему пути:
- учесть символы, не соответствующие желаемому образцу (например:
<[^>]*>
для вышеописанного случая) - избавить от жадности, добавив определении квантификатора, как нежадного:
*?
— «не жадный» («ленивый») эквивалент*
+?
— «не жадный» («ленивый») эквивалент+
{n,}?
— «не жадный» («ленивый») эквивалент{n,}
.*?
— «не жадный» («ленивый») эквивалент.*
Все вышенаписанное хочу дополнить синтаксисом расширенных регулярных выражений:
Регулярные выражения в POSIX аналогичны традиционному Unix-синтаксису, но с добавлением некоторых метасимволов:
+
Плюс указывает на то, что предыдущий символ или группа может повторяться один или несколько раз. В отличие от звёздочки, хотя бы одно повторение обязательно.
?
Знак вопроса делает предыдущий символ или группу необязательной. Другими словами, в соответствующей строке она может отсутствовать, либо присутствовать ровно один раз.
|
Вертикальная черта разделяет альтернативные варианты регулярных выражений. Один символ задаёт две альтернативы, но их может быть и больше, достаточно использовать больше вертикальных чёрточек. Необходимо помнить, что этот оператор использует максимально возможную часть выражения. По этой причине, оператор альтернативы чаще всего используется внутри скобок.
Также было отменено использование обратной косой черты: \{…\} становится {…} и \(…\) становится (…).
В завершение поста, приведу некоторые примеры использования regex:
[regex@regexp k-max.name]$ cat text1 1 apple 2 pear 3 banana [regex@regexp k-max.name]$ grep p text1 1 apple 2 pear [regex@regexp k-max.name]$ grep pea text1 2 pear [regex@regexp k-max.name]$ grep "p*" text1 1 apple 2 pear 3 banana [regex@regexp k-max.name]$ grep "pp*" text1 1 apple 2 pear [regex@regexp k-max.name]$ grep "x" text1 [regex@regexp k-max.name]$ grep "x*" text1 1 apple 2 pear 3 banana [regex@regexp k-max.name]$ cat text1 | grep "l\|n" 1 apple 3 banana [regex@regexp k-max.name]$ echo -e "find an\n* here" | grep "\*" * here [regex@regexp k-max.name]$ grep "pp\+" text1 # строки, с содержанием одной р и 1 и более р 1 apple [regex@regexp k-max.name]$ grep "pl\?e" text1 1 apple 2 pear [regex@regexp k-max.name]$ grep "pl\?e" text1 # pe с возможным символом l 1 apple 2 pear [regex@regexp k-max.name]$ grep "p.*r" text1 # p, в строках где есть r 2 pear [regex@regexp k-max.name]$ grep "a.." text1 # строки с a, за которой следует как минимум 2 символа 1 apple 3 banana [regex@regexp k-max.name]$ grep "\(an\)\+" text1 # Поиск более повторения an 3 banana [regex@regexp k-max.name]$ grep "an\(an\)\+" text1 # поиск 2х повторений an 3 banana [regex@regexp k-max.name]$ grep "[3p]" text1 # поиск строк, где есть 3 или p 1 apple 2 pear 3 banana [regex@regexp k-max.name]$ echo -e "find an\n* here\nsomewhere." | grep "[.*]" * here somewhere. [regex@regexp k-max.name]$ # Ищет символы от 3 до 7 [regex@regexp k-max.name]$ echo -e "123\n456\n789\n0" | grep "[3-7]" 123 456 789 [regex@regexp k-max.name]$ # Ищем цифру, за которой до конца строки нет букв n и r [regex@regexp k-max.name]$ grep "[[:digit:]][^nr]*$" text1 1 apple [regex@regexp k-max.name]$ sed -e '/\(a.*a\)\|\(p.*p\)/s/a/A/g' text1 # замена а на А во всех строках, где после а идет а или после р идет р 1 Apple 2 pear 3 bAnAnA [regex@regexp k-max.name]$ sed -e '/^[^lmnXYZ]*$/s/ear/each/g' text1 # замена ear на each в строках не начинающихся на lmnXYZ 1 apple 2 peach 3 banana [regex@regexp k-max.name]$ echo "First. A phrase. This is a sentence." |\ # замена последнего слова в предложении на LAST WORLD. &amp;gt; sed -e 's/ [^ ]*\./ LAST WORD./g' First. A LAST WORD. This is a LAST WORD.
С Уважением, Mc.Sim!
Другие материалы в категории Linux
Теги: bash, Linux, regex, sed, UNIX, команды, основыwww.k-max.name
Regexp — это «язык программирования». Основы / Habr
Несколько лет назад я думал, что regexp осуществляет линейный поиск по тексту, но какое моё удивление было, когда я понял, что это не так. Тогда я убедился на собственном опыте, что от простой смены местами а и b в схеме (…a…)|(…b…) поменялся полностью результат.Поэтому сейчас я расскажу, как на самом деле работает regexp.
Поняв эти простые принципы и как оно работает, вы сможете писать любые запросы.
Для примера, я разберу сложную при первом приближении, но на самом деле простейшую задачу – выявление всех строк в кавычках.
Ветвления в regexp
Регулярные выражения работают по схеме направленного дерева.Например ветвление (a …)|(b …)|(c …) можно записать:
If (char=='a'){
...
}else if (char =='b'){
...
}else if (char =='c'){
...
}
Именно поэтому перестановка местами 'b ...' и 'a ...'
влияет на результат – потому что здесь расставляются приоритеты выполнения (в каком порядке поставишь, так и будет выполняться).
Именно поэтому стоит следить за тем, что приоритетнее и стараться условия делать оптимально вытесняющими друг друга.
Причём выполняться всё, что за ‘a’ будет при условии того, что всё до ‘a’ (включая ‘a’) выполнено.
Пишем парсер кавычек
Рассмотрим простой пример.Давайте возьмём подопытную строку 123"ABC\"D\EF"GHI""\"KEY
и начнём над ней издеваться:
Первое, что появляется в голове — /".*"/
выражение. Оно будет действовать по алгоритму:
1) Производим поиск первой одной "
2) Пока у нас любой символ (в том числе и "
), мы проходим дальше
3) В конце должна быть тоже "
В результате, правильно, мы получили "ABC\"D\EF"GHI""\"
.
То есть нашли первую кавычку. Дальше, пока выполнялось условие брали следующие символы (в том числе и "
) и делали, пока последней не оказалась "
.
Теперь давайте усовершенствует алгоритм – сделаем, чтобы искался любой символ, исключая "
.
Наше регулярное выражение превратилось в /"[^"]*"/
. Оно будет действовать по алгоритму:
1) Производим поиск первой одной "
2) Пока у нас любой символ не равный "
, мы проходим дальше
3) Наткнулись на "
– конец.
Результат стал более верным – были выбраны "ABC\", "GHI", "\"
.
Теперь надо нам определять символы \"
оставлять их внутри, не считая это концом.
Для этого необходимо изменить условие [^"]
, добавив туда ещё одно сравнение с \"
.
Выглядеть это будет теперь так — /"([^"]|(\\\\"))*"/
.
Мы добавили в условие \\\\"
. Почему четыре '\'
? Потому что каждые две \\ в строке = одной \, то есть мы записали \\ в строку запроса, да и в regexp используются выражения \w,\d,\s итд, по этому, чтобы поставить одну \, необходимо использовать \\.
Но наше выражение ещё не будет работать.
Почему не будет? Не будет работать, потому что сначала происходит условие [^"]
, а затем, если оно не выполняется, то идёт сравнение с \"
:
1) Производим поиск первой одной "
2) Пока у нас любой символ не равный "
, мы проходим дальше,
если он равен "
(не выполнилось предыдущее условие), мы сравниваем его c \ (само собою он не равен)
3) Наткнулись на "
– конец.
По этому правильно будет поменять условия местами — /»((\\\\»)|[^»])*»/, чтобы сначала проверялся \"
, а потом любой другой символ не "
.
Теперь всё правильно работает и результат выбирает "ABC\"D\EF", ""
. Похоже на магию, да? Алгоритм заработал правильно.
Сразу скажу, что вариант [^"\\\\]|(\\\\")
не подходит, потому что когда алгоритм найдёт \, он перейдёт ко втором условию \"
(за \ должен быть "
), что не выполнится в нашем случае \E. Для этого необходимо будет поставить условие — если после \ идёт "
, то пропускаем символ, иначе идём дальше. То есть выражение приобретет вид /"([^"\\\\]|(\\\\(")?))*"/
.
Совершенствуем алгоритм
Давайте добавим парсинг символа ‘.В регулярных выражениях можно использовать найденные символы в будующих проверках – этим мы и воспользуемся:
Начнём наше выражение с поиска любого символа кавычек/апострофа /(["'])...
– найденная кавычка попадёт у нас в спец-переменную \1, которую мы можем дальше использовать в проверке. В данном случае, у нас туда будет попадать один символ — либо "
, либо ‘. В конце, чтобы проверить закрытие это кавычки, необходимо использовать ...(\1)/
. Внутри, проверять не на отсутствие "
, а на отсутствие \1.
Оптимизируем немного код и получаем /(["\'])(\\\\\1|.)*?\1/
. Надо заметить, что я использовал ? (lazy) в выражении – для добавления в условие последней \1 – то есть, сейчас ко всему прочему происходит ещё проверка на "
.
Почему я сделал это вместо [^\1]? Потому что \1 не работает в [].
Сейчас код делает следующее:
1) Производим поиск первой одной "
или ‘ (записываем его в \1)
2) Следующий символ "
или ‘?
если нет, тогда следующие два символа равны \"
или \'
(в зависимости от начала)
если нет, тогда просто пропускаем символ
3) Наткнулись на "
– конец.
И выражение 1'2'a3"A'BC\"DEF"GHI""\"KEY
парсится в '2', "A'BC\"DEF", ""
.
Данное выражение можно использовать для выделения строковых областей внутри любых объектов.
Например, function:
function a(){
b="{}"
;
}
Добавляем фигурные скобки в выражение /{((["\'])(\\\\.|[^\\\\])*?\2|.)*?}/
. Это выражение теперь выберет {b="{}";}
. Так как появились ещё одни скобки в выражении, то \1 сдвинулось в \2 – следите за этим обязательно.
Upd. Я забыл упомянуть про обратный ход. Есть такая движуха, когда алгоритм ничего не находит, двигаясь прямо :). По этому лучше вместо . использовать [^\\\\]. (см. последний пример) В этом случае, нахождения в строке «\» не произойдёт, как и должно быть.
habr.com
Использование регулярных выражений в Ruby / Habr
Регулярные выражения — спасение от всех бед для одних и ночной кошмар для других разработчиков, а если говорить объективно, то это мощнейший инструмент, требующий, однако, большой осторожности при применении. Регулярные выражения (регексы, регекспы, регулярки) в языке Ruby основаны на синтаксисе Perl 5 и потому в основных чертах знакомы всем, кто использовал Perl, Python или PHP. Но Ruby тем и хорош, что каждый компонент языка реализован со своим собственным подходом, упрощающим использование данного инструмента и увеличивающим его мощность. В предлагаемой мной небольшой статье рассматриваются особенности регулярок в Ruby и их применение в различных операторах.В Ruby все — объект
Прежде всего стоит отметить, что регулярное выражение является объектом соответствующего класса. Соответственно, его можно создавать через вызов new и объединять(union).
r1 = Regexp.new “a”
r2 = Regexp.new “b”
ru = Regexp.union r1, r2
Выражение, получившееся в результате объединения будет соответствовать строкам, соответствующим хотя бы одному из объединяемых шаблонов.
Оператор сопоставления регулярки со строкой возвращает индекс первого совпадения или nil, но нам во многих случаях нужна и другая информация о найденном совпадении. Можно, как и Perl, воспользоваться специальными переменными, $~, $’, $& и так далее. Если переменные $1, $2, …, соответствующие группам, запомнить довольно просто, то как люди вообще пользуются остальными для меня всегда оставалось загадкой. Поэтому в Ruby конечно же есть другой подход — можно использовать метод Regexp.last_match
“abcde” =~ /(b)(c)(d)/
Regexp.last_match[0] # "asd"
Regexp.last_match[1] # "b"
Regexp.last_match[2] # "c"
Regexp.last_match[3] # "d"
Regexp.last_match.pre_match # "a"
Regexp.last_match.post_match # "e"
Поименованные группы
Ruby, начиная с версии 1.9 поддерживает синтаксис поименованных групп:
"a reverse b".gsub /(?<first>\w+) reverse (?<second>\w+)/, '\k<second> \k<first>' # “b a”
Этот же пример демонстрирует и использование обратных ссылок, но эта возможность и так есть уже во всех современных реализациях PCRE.
\k<group_name> — эта специальная последовательность по сути является аналогом обратных ссылок для именованных групп.
\g<group_name> — последовательность, соответствующая повторению ранее заданной именованной группы. Различие между ними просто показать на примере:
"1 1" =~ /(?<first>\d+) \k<first>/ # 0
"1 2" =~ /(?<first>\d+) \k<first>/ #nil
"1 a" =~ /(?<first>\d+) \k<first>/ #nil
"1 1" =~ /(?<first>\d+) \g<first>/ # 0
"1 2" =~ /(?<first>\d+) \g<first>/ # 0
"1 a" =~ /(?<first>\d+) \g<first>/ #nil
Получить совпадения связанные с этими группами также можно через объект MatchData:
Regexp.last_match[:first]
Другие способы проверить соответствие
Кроме традиционного =~ в Ruby есть и другие способы проверить строку на совпадение с регулярным выражением. В частности, для этого предназначен метод match, который особенно хорош тем, что может вызываться применительно как к объекту класса String, так и к экземпляру Regexp. Но и это не все. Получить совпадение строки регуляркой можно обычным методом индексирования:
"abcde"[/bc?f?/] # "bc"
, а также методом slice:
"abcde".slice(/bc?f?/) # "bc"
Кроме того, есть и еще один, на вид не самый логичный способ:
/bc?f?/ === "abcde" # true
Вряд ли кто-то будет использовать подобный синтаксис, но и этому замечательному свойству языка Ruby есть применение, о чем будет написано далее.
Применение регулярок в различных функциях
Одним из самых полезных применений регулярных выражений в Ruby, которое встречается однако не так часто, является их использование в операторе case. Пример:
str = 'september'
case str
when /june|july|august/:
puts "it's summer"
when /september|october|november/:
puts "it's autumn"
end
Все дело в том, что сравнение в case как раз выполняется вышеупомянутым оператором ===(подробнее здесь), что и позволяет очень лаконично и элегантно использовать регекспы в таких случаях.
Также регулярки можно использовать в функции split. Пример с ruby-doc:
"1, 2.34,56, 7".split(%r{,\s*}) # ["1", "2.34", "56", "7"]
Один из способов получения списка слов из строки с помощью этой функции:
“one two three”.split(/\W+/)
Для работы с кириллическими строками:
"строка, из которой нужно получить список слов".split(/[^[:word:]]+/) # ["строка", "из", "которой", "нужно", "получить", "список", "слов"]
(ruby 1.9 only)
Для разделения строки на части иногда гораздо удобнее использовать метод scan. Предудыщий пример с использованием этого метода:
"строка, из которой нужно получить список слов".scan(/[[:word:]]+/) # ["строка", "из", "которой", "нужно", "получить", "список", "слов"]
(ruby 1.9 only)
Функция sub, выполняющая замену первого вхождения подстроки, также может принимать на вход объект Regexp:
"today is september 25".sub(/\w+mber/, 'july') # "today is july 25"
Аналогично можно использовать регулярные выражения в методах sub!, gsub и gsub!..
Метод partition, разделяющий строку на 3 части, также может использовать регулярное выражение в качестве разделителя:
"12:35".partition(/[:\.,]/) # ["12", ":", "35"]
Аналогично можно использовать регулярные выражения в методе rpartition.
Методы index и rindex также могут работать с регулярками, возвращают они, понятное дело, индексы первого и последнего их вхождения в строку.
Дополнительная литература
1. Фридл — Регулярные выражения
2. Флэнаган, Мацумото — Язык программирования Ruby
3. Ruby-doc class Regexp
habr.com