Перейти к содержанию

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

Регулярные выражения (их еще называют regexp, или regex от англ. regular expressions)) — это механизм для поиска и замены текста. В строке, файле, нескольких файлах... Их используют разработчики в коде приложения, тестировщики в автотестах, да просто при работе в командной строке! Кстати, утилита grep разрабатывалась именно для её использования с регулярными выражениями и само название — это аббревиатура из всех слов (search globally for lines matching the regular expression, and print them).

Простыми словами regexp - список сопоставлений (шаблонов), одного или нескольких, объединённым в группы и нет, для которых будет найден необходимый фрагмент текста. Ниже я приведу полную таблицу всех операторов регулярных выражений, но сейчас, что бы познакомится хватит нескольких.

Небольшая шпаргалка:

Оператор Описание
. Любой символ кроме новой строки.
\w \d \s Любое слово, любая цифра, любой символ включая пробел
\W \D \S Любое не слово, любая не цифра и не пробел
[abc] Любое сочетание a, b, или c
[^abc] Все кроме a, b, или c
[a-g] Любая буква в диапазоне от a до g
. * \ Экранировать специальные символы
(abc) Создание группы
(?:abc) Исключить группу
(?=abc) Включить в резлультат
(?!abc) Исключить из результата
a* b+ c? Любой символ и более, только первый и далее, любой и следующий за ним
ab bc

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

#  echo "Hello world! Count down in: 5,4,3,2,1!" | grep -Po '\d+'
Вывод:
5
4
3
2
1

Отлично, мы выбрали только цифры, так как следует из шпаргалки оператор \d выводит любую цифру, а знак +говорит что бы в последовательность вошли все цифры которые встрется после первого совпадения. Теперь попробуем исключить цифры из текста:

# echo "Hello world! Count down in: 5,4,3,2,1!" | grep -Po '([^\d]+)'
Вывод:
Hello world! Count down in:
,
,
,
,
!

И так, выражение стало сложнее, но давайте разберем его. Скобки (), означают что мы создал группу, как видно из шпаргалки ([^abc]), мы взяли все что не является цифрой получили [^\d]+, знак + означает что за первым совпадением нужно забрать все… кроме цифр. Вот и все.

А теперь уберем все лишние знаки что остались после удаления цифр:

# echo "Hello world! Count down in: 5,4,3,2,1!" | grep -Po '([^\d]+)([^\w])'
Вывод:
Hello world! Count down in:

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

. - точка соответствует любому символу, например, gr.y найдет строки "gray", "green", "groom". ^ - символ начала строки, например, ^hello найдет строки, начинающиеся с "hello". \(** - символ конца строки, например, world\) найдет строки, заканчивающиеся на "world". **[] - определяет класс символов, например, [aeiou] найдет любую гласную букву, [0-9] найдет любую цифру. [^] - отрицание класса символов, например, [^0-9] найдет любой символ, кроме цифр. ** * ** - символ, обозначает необязательный элемент, например, abc* найдет "ab", "abc", "abcc", "abccc", и т.д. + - символ, действующий почти так же, как , но требует совпадения с предыдущим элементом не менее одного раза, например, abc+ найдет "abc", "abcc", "abccc", и т.д. ? - символ, означающий совпадение с элементом ноль или один раз. Иными словами совпадение с предыдущим элементом необязательно, например, ab?c найдет "ac" и "abc". () - используются для группировки выражений, например, (ab)+ найдет "ab", "abab", "ababab", и т.д. *** - используется для экранирования специальных символов, например, . найдет точку в тексте.

Квантификаторы

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

a|b - или a, или b
? - ноль или один
+ - один или больше
* - ноль или больше
{N} - ровно N раз (здесь N - число)
{N,} - N или больше раз (N - число)
{N,M} - от N до M раз (N и M - числа, при этом N < M)
*? - ноль или больше, но после первого совпадения поиск нужно прекратить

Например, следующее регулярное выражение соответствует и строке «Hello», и строке «Goodbye»:

Hello|Goodbye
В то время как
Hey?
Может означать как отсутствие y, так и одно вхождение y, и таким образом весь шаблон может соответствовать и «He», и «Hey».

Еще пример:

Hello{1,3}
Этот шаблон соответствует «Hello», «Hellooo», но не «Helloooo», потому что буква «о» может встречаться от 1 до 3 раз.

Квантификаторы можно комбинировать:

He?llo{2}
Здесь мы ищем строки, в которых «e» нет или встречается 1 раз, а «o» встречается ровно 2 раза. Таким образом, этот шаблон соответствует словам «Helloo» и «Hlloo».

Наборы

Квадратные скобки позволяют искать совпадение по целому набору символов, указанному в скобках. Например, шаблон

My favorite vowel is [aeiou]
Совпадет со строками:
My favorite vowel is a
My favorite vowel is e
My favorite vowel is i
My favorite vowel is o
My favorite vowel is u
И ни с чем другим. [aeiou] - это набор, в регулярном выражении означающий «любой из указанных символов».

Вот список самых распространенных наборов:

[A-Z] - совпадает с любой буквой в верхнем регистре, от «A» до «Z» [a-z] - совпадает с любой буквой в нижнем регистре, от «a» до «z» [0-9] -любая цифра [asdf] - совпадает с «a», «s», «d» или «f» [^asdf] - совпадает с любым символом кроме «a», «s», «d» или «f»

Эти наборы можно комбинировать:

[0-9A-Z] - любой символ, являющийся либо цифрой, либо буквой от A до Z [^a-z] - любой символ, не являющийся буквой латинского алфавита в нижнем регистре

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

Не каждый символ можно так легко идентифицировать. Скажем, найти буквы с использованием regex легко, а как насчет символа новой строки?

Примечание. Символ новой строки - это символ, который вы вводите, когда нажимаете Enter и переходите на новую строку.

. - любой символ \n - символ новой строки \t - символ табуляции \s - пробельный символ (включая \t, \n и некоторые другие) \S - не-пробельный символ \w - любой «словообразующий» символ (буквы латинского алфавита в верхнем и нижнем регистре, цифры 0-9 и символ подчеркивания _) \W - любой «несловообразующий» символ (класс символов, обратный классу \w) \b - граница слова, разделяет \w и \W, т. е. словообразующие и несловообразующие символы. Граница слова соответствует позиции, где за символом слова не следует другой символ слова. \B - несловообразующая граница (класс, обратный \b). Несловообразующая граница соответствует позиции, в которой предыдущий и следующий символы являются символами одного типа: либо оба должны быть словообразующими символами, либо несловообразующими. Начало и конец строки считаются несловообразующими символами. ^ - начало строки $ - конец строки \ - символ «\» в буквальном значении Допустим, вы хотите удалить каждый символ, с которого начинается новое слово в строке. Вы можете использовать следующее регулярное выражение для поиска этих символов:

\s.
А затем найденные символы можно заменить пустой строкой. Таким образом строка
Hello world how are you
Превратится в
Helloorldowreou

Комбинирование наборов

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

[A-Z]|\s
Но \s можно поместить и внутрь набора:
[A-Z\s]

Границы слова

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

Допустим, у вас есть строка «This is a string». Вы можете предположить, что символ границы слова соответствует пробелам между словами, но это не так. Он соответствует тому, что находится между буквой и пробелом.

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

\b\w+\b
Это регулярное выражение интерпретируется следующим образом: «Граница слова, за которой следует один или больше словообразующих символов, за которыми следует другая граница слова».

Начало и конец строки

Еще два важных токена - ^ и $. Они означают начало и конец строки соответственно.

То есть, если вы хотите найти первое слово в строке, вы можете написать следующее выражение:

^\w+

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

Аналогично, если вы хотите найти последнее слово в строке, ваше регулярное выражение может выглядеть так:

\w+$
Но то, что символ $ обычно заканчивает строку, не означает, что после него не может идти других символов.

Допустим, мы хотим найти каждый пробельный символ между новыми строками для создания базового минификатора JavaScript-кода.

Мы можем написать следующее выражение, чтобы найти все пробелы после конца строки:

$\s+

Экранирование символов

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

Допустим, у вас есть строка в тексте статьи:

"Символ новой строки - '\n'"

Или вы хотите найти вообще все упоминания «\n» в тексте. Тогда в шаблоне символ \n нужно «экранировать»: поставить перед ним обратную косую черту:

\\n

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

Вы можете использовать regex для поиска и замены содержимого файлов. Скажем, вы хотите заменить любое приветствие на прощание. Можно сделать это так:

function  youSayHelloISayGoodbye(str)  {
    str = str.replace("Hello", "Goodbye");
    str = str.replace("Hi", "Goodbye");
    str = str.replace("Hey", "Goodbye"); str = str.replace("hello", "Goodbye");
    str = str.replace("hi", "Goodbye");
    str = str.replace("hey", "Goodbye");

    return str;
}
Но можно и проще, с использованием regex:

function  youSayHelloISayGoodbye(str)  {
    str = str.replace(/[Hh]ello|[Hh]i|[Hh]ey/, "Goodbye");

    return str;
}

Флаги в regex

Флаг - это модификатор существующего регулярного выражения. При определении regex флаги всегда добавляются после замыкающего слэша.

Вот небольшой список доступных флагов:

  • g - глобально, больше одного совпадения
  • m - заставляет $ и ^ соответствовать каждой новой строчке отдельно
  • i - делает regex нечувствительным к регистру

Мы можем взять наше регулярное выражение:

/[Hh]ello|[Hh]i|[Hh]ey/
и переписать его, применив флаг нечувствительности к регистру:

/Hello|Hi|Hey/i
Это регулярное выражение будет соответствовать следующим словам:

Hello
HEY
Hi
HeLLo
Также оно будет соответствовать любому другому варианту чередования заглавных и строчных букв.

Флаг глобального поиска для замены строк

Как уже говорилось, если вы производите замену при помощи regex без всяких флагов, заменен будет только первый результат поиска:

let str = "Hello, hi there!";
str = str.replace(/[Hh]ello|[Hh]i|[Hh]ey/, "Goodbye");
console.log(str); // В выводе будет "Goodbye, hi there"
JavaScript

Но если вы добавите флаг глобального поиска, будут найдены все соответствия шаблону:

let str = "Hello, hi there!";
str = str.replace(/[Hh]ello|[Hh]i|[Hh]ey/g, "Goodbye");
console.log(str); // В выводе будет "Goodbye, Goodbye there"