Регулярные выражения (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?
Еще пример:
Hello{1,3}
Квантификаторы можно комбинировать:
He?llo{2}
Наборы
Квадратные скобки позволяют искать совпадение по целому набору символов, указанному в скобках. Например, шаблон
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
Вот список самых распространенных наборов:
[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
[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;
}
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"