Regex
Регулярные выражения (regex или regexp) очень эффективны для извлечения информации из текста.
Якоря — ^ и $
^Привет соответствует строке, начинающейся с Привет -> тест пока$ соответствует строке, заканчивающейся на пока ^Привет пока$ точное совпадение (начинается и заканчивается как Привет пока) воробушки соответствует любой строке, в которой есть текст
Квантификаторы — * + ? и {}
abc* соответствует строке, в которой после ab следует 0 или более символов c -> тест - https://regex101.com/r/cO8lqs/1 abc+ соответствует строке, в которой после ab следует один или более символов c abc? соответствует строке, в которой после ab следует 0 или один символ c abc{2} соответствует строке, в которой после ab следует 2 символа c abc{2,} соответствует строке, в которой после ab следует 2 или более символов c abc{2,5} соответствует строке, в которой после ab следует от 2 до 5 символов c a(bc)* соответствует строке, в которой после ab следует 0 или более последовательностей символов bc a(bc){2,5} соответствует строке, в которой после ab следует от 2 до 5 последовательностей символов bc
Оператор ИЛИ — | или []
a(b|c) соответствует строке, в которой после a следует b или c -> тест a[bc] как и в предыдущем примере
Символьные классы — \d \w \s и .
\d соответствует одному символу, который является цифрой -> тест \w соответствует слову (может состоять из букв, цифр и подчёркивания) -> тест \s соответствует символу пробела (включая табуляцию и прерывание строки) . соответствует любому символу -> тест
Используйте оператор .
с осторожностью, так как зачастую класс или отрицаемый класс символов (который мы рассмотрим далее) быстрее и точнее.
У операторов \d
, \w
и \s
также есть отрицания ― \D, \W
и \S
соответственно.
Например, оператор \D
будет искать соответствия противоположенные \d
.
\D соответствует одному символу, который не является цифрой -> тест
Некоторые символы, например ^.[$()|*+?{\
, необходимо выделять обратным слешем \
.
\$\d соответствует строке, в которой после символа $ следует одна цифра -> тест
Непечатаемые символы также можно искать, например табуляцию \t
, новую строку \n
, возврат каретки \r
.
Флаги
Мы научились строить регулярные выражения, но забыли о фундаментальной концепции ― флагах.
Регулярное выражение, как правило, записывается в такой форме /abc
/, где шаблон для сопоставления выделен двумя слешами /
. В конце выражения, мы определяем значение флага (эти значения можно комбинировать):
- g (global) ― не возвращает результат после первого совпадения, а продолжает поиск с конца предыдущего совпадения.
- m (multi line) ― с таким флагом, операторы
^
и$
вызовут совпадение в начале и конце строки ввода (line), вместо строки целиком (string). - i (insensitive) ― делает выражение регистронезависимым (например, /aBc/i соответствует AbC).
Средний уровень
Скобочные группы ― ()
a(bc) создаём группу со значением bc -> тест a(?:bc)* оперетор ?: отключает группу -> тест a(?<foo>bc) так, мы можем присвоить имя группе -> тест
Этот оператор очень полезен, когда нужно извлечь информацию из строк или данных, используя ваш любимый язык программирования. Любые множественные совпадения, по нескольким группам, будут представлены в виде классического массива: доступ к их значениям можно получить с помощью индекса из результатов сопоставления.
Если присвоить группам имена (используя (?<foo>...)
), то можно получить их значения, используя результат сопоставления, как словарь, где ключами будут имена каждой группы.
Скобочные выражения ― []
[abc] соответствует строке, которая содержит либо символ a или a b или a c -> такой же эффект от a|b|c -> тест [a-c] то же, что и выше [a-fA-F0–9] строка, представляющая одну шестнадцатеричную цифру без учёта регистра -> тест [0–9]% строка, содержащая символ от 0 до 9 перед знаком % [^a-zA-Z] строка, которая не имеет буквы от a до z или от A до Z. В этом случае ^ используется как отрицание в выражении -> тест
Помните, что внутри скобочных выражений все специальные символы (включая обратную косую черту \
) теряют своё служебное значение, поэтому нам ненужно их экранировать.
Жадные и ленивые сопоставления
Квантификаторы ( * + {}
) ― это «жадные» операторы, потому что они продолжают поиск соответствий, как можно глубже ― через весь текст.
Например, выражение <.+>
соответствует <div>simple div</div>
в This is a <div> simple div</div> test
. Чтобы найти только тэг div
― можно использовать оператор ?
, сделав выражение «ленивым»:
<.+?> соответствует любому символу, один или несколько раз найденному между < и >, расширяется по мере необходимости -> тест
Обратите внимание, что хорошей практикой считается не использовать оператор .
, в пользу более строгого выражения:
<[^<>]+> соответствует любому символу, кроме < или >, один или более раз встречающемуся между < и > -> тест
Продвинутый уровень
Границы слов ― \b и \B
\babc\b выполняет поиск слова целиком -> тест
\b
― соответствует границе слова, наподобие якоря (он похож на $
и ^
), где предыдущий символ ― словесный (например, \w
), а следующий ― нет, либо наоборот, (например, это может быть начало строки или пробел).
\B
― соответствует несловообразующей границе. Соответствие не должно обнаруживаться на границе \b
.
\Babc\B соответствует, только если шаблон полностью окружён словами -> тест
Обратные ссылки — \1
([abc])\1 \1 соответствует тексту из первой захватываемой группы -> тест ([abc])([de])\2\1 можно использовать \2 (\3, \4, и т.д.) для определения порядкового номера захватываемой группы -> тест (?<foo>[abc])\k<foo> мы присвоили имя foo группе, и теперь ссылаемся на неё используя ― (\k<foo>). Результат, как и в первом выражении -> тест
Опережающие и ретроспективные проверки — (?=) and (?<=)
d(?=r) соответствует d, только если после этого следует r, но r не будет входить в соответствие выражения -> тест (?<=r)d соответствует d, только если перед этим есть r, но r не будет входить в соответствие выражения -> тест
Вы можете использовать оператор отрицания !
d(?!r) соответствует d, только если после этого нет r, но r не будет входить в соответствие выражения -> тест (?<!r)d соответствует d, только если перед этим нет r, но r не будет входить в соответствие выражения -> тест
Заключение
Как вы могли убедиться, области применения регулярных выражений разнообразны. Я уверен, что вы сталкивались с похожими задачами в своей работе (хотя бы с одной из них), например такими:
- Валидация данных (например, правильно ли заполнена строка time)
- Сбор данных (особенно веб-скрапинг, поиск страниц, содержащих определённый набор слов в определённом порядке)
- Обработка данных (преобразование сырых данных в нужный формат)
- Парсинг (например, достать все GET параметры из URL или текст внутри скобок)
- Замена строк (даже во время написания кода в IDE, можно, например преобразовать Java или C# класс в соответствующий JSON объект, заменить “;” на “,”, изменить размер букв, избегать объявление типа и т.д.)
- Подсветка синтаксиса, переименование файла, анализ пакетов и многие другие задачи, где нужно работать со строками (где данные не должны быть текстовыми).