Coding: основы регулярных выражений (regexp)

https://regexr.com/ – хороший сайт для создания regexp

http://www.pcre.org/original/doc/html – прекрасная документация к самому популярному синтаксису regexp

  • Ускорить regexp могут детерминированные конечные автоматы, типо таких, которые реализует Intel Hyperscan 

Regexp используются очень широко, в том числе в сетевом оборудовании:

  • В целом регулярные выражения (а точнее pattern matching) используются везде в ngfw – антивирус, IPS, DPI, URLF

“We have worked with Mellanox for many years to integrate our RXP regular expression processor into their advanced line of BlueField I/O Processing Units (IPUs). Now as part of Mellanox, we will be able to achieve new capabilities for cyber intelligence, intrusion detection and protection, and advanced data analytics applications,” said Noel McKenna, CEO, Titan IC.

Our customers will benefit from the deep analytics and enhanced security that will be delivered by the integration of our best-in-class Ethernet and InfiniBand products and world-leading deep packet inspection and analytics technologies from Titan IC.
cisco3850# show interfaces Te1/4 | i packets .*put
1385 packets input, 193568 bytes, 0 no buffer
0 packets output, 0 bytes, 0 underruns
Router# sh run | include ^interface|^ ip address
interface Tunnel0
 ip address 10.255.0.1 255.255.255.0
interface Tunnel3
interface Tunnel4
interface Ethernet0/0
 ip address 15.0.0.1 255.255.255.0
interface Ethernet0/1
 ip address 10.1.1.1 255.255.255.0
interface Ethernet0/2
 ip address 10.101.1.1 255.255.255.0
interface Ethernet0/3
interface Ethernet1/0
interface Ethernet1/1
Router# show ip bgp regexp 108$

BGP table version is 1738, local router ID is 172.16.72.24
Status codes: s suppressed, * valid, > best, i - internal
Origin codes: i - IGP, e - EGP, ? - incomplete

   Network          Next Hop          Metric LocPrf Weight Path
*  172.16.0.0       172.16.72.30                         0 109 108 ?
*  172.16.1.0       172.16.72.30                         0 109 108 ?
*  172.16.11.0      172.16.72.30                         0 109 108 ?
*  172.16.14.0      172.16.72.30                         0 109 108 ?

Работа с регулярными выражениями в конкретных языка/инструментах есть тут:

  • Ruby
  • Python поиск по regexp
  • IP regexp bash
check_ip() {
  IP_REGEX='^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
  printf '%s' "$1" | tr -d '\n' | grep -Eq "$IP_REGEX"
}

regexp нужны для парсинга/поиска вхождения данных в строку. Описываются формальным языком, который интерпретируется движком регулярных выражений. По сути это язык программирования символами, старый и довольно “стремный” – одни скобки означают набор символов, другие – начало и конец извлечения данных. В программировании рекомендуют  их использовать умеренно, т.к. читаемость “кода” регулярного выражения может быть довольно сложной задачей. Так же как и другой код, его обязательно нужно тестировать.

A regular expression is written in a formal language that can be interpreted by a regular expression processor.
They're kind of fun, like a whole programming language in characters.
This is a very, very cryptic and arcane language: brackets means the beginning of a set of characters, parenthesis means start extracting, other parenthesis means stop extracting.
I would suggest you don't overuse them. 
It's really important that you test your code, so that you see kind of weird anomalies.
Charles R. Severance

При использовании regexp всегда надо помнить, что если операция простая и осуществима методами строк – то скорей всего использование regexp излишне и приведет только к более долгому исполнению (на больших объемах). Зачастую методы поиска строк используются как предфильтр (своего рода guardian pattern) перед регулярным выражением (например, можно встретить запрет использования только regexp без предфильтра content в правилах написания сигнатур для snort) – отлавливаем только строки, которые потенциально могут подпадать под regexp, а далее уже анализируем движком regexp.

- Always put pcre checks after one or more content checks
- Never start your rule with a pcre keyword or use pcre as the only keyword in the rule
Спец. символы

многое взято отсюда и отсюда

^ – начало строки или отрицание при использовании в [] (см. ниже и примеры в языках программирования)

$ – конец строки

* – астериск/звездочка (asterisk) означает любое количество символов в строке, предшествующих “звездочке”, в том числе и нулевое число символов.

Выражение "1133*" -- означает 11 + один или более символов "3" + любые другие символы: 113, 1133, 113312, и так далее.

+ – матчит любое количество символов в строке, предшествующих плюсу, но не нулевое число символов (в отличии от *), часто используется для обозначения одного продолженного элемента – напр. извлечь не каждый символ integer из строки, а все полные (продолженные) числа

# python example 1
line = "sdf777sdf fdsf mm 5554351 fdfm mg 77 sdfsdf 6 fdsf2s"
res = re.findall(r'\d', line)
['7', '7', '7', '5', '5', '5', '4', '3', '5', '1', '7', '7', '6', '2']

# python example 2
line = "sdf777sdf fdsf mm 5554351 fdfm mg 77 sdfsdf 6 fdsf2s"
res = re.findall(r'\d+', line)
['777', '5554351', '77', '6', '2']

. – точка совпадает с любым одиночным символом, за исключением символа перевода строки (\n). Сделать так, чтобы срабатывала и на \n обычно можно с помощью флага, например в Python это DOTALL.

Выражение "13." будет означать 13 + по меньшей мере один любой символ (включая пробел): 1133, 11333, но не 13 (отсутствуют дополнительные символы).

.* – точка совместно со звездочкой означают подпадание любого (в том числе ненулевого) количества символов

? – вопросительный знак означает что знак перед вопросительным является опциональным (может отсутствовать), так же с помощью  вопросительного знака можно матчить более тонко/коротко/менее жадно (non-gready matching). По умолчанию символы повторения (напр. * и +) жадные (gready) и будут всегда отображать наиболее длинный match.

Greedy Matching says you're going to get back the larger thing.

Надо заматчить From: в строке:
"From: Using the : character"
# Пример regexp для решения задачи
^F.+?: # полный regexp, где:
^ - начало строки
. - любой символ
+ - минимум одно подпадание
? - non-gready (короткое подпадание)
: - конкретный символ

| – параллель используется как оператор “или”

cisco1#show ip interface brief | i Inter|t1/0/[2][0-4]
Interface IP-Address OK? Method Status Protocol
FastEthernet1/0/20 192.168.1.49 YES NVRAM administratively down down 
FastEthernet1/0/21 192.168.119.113 YES NVRAM up up 
FastEthernet1/0/22 unassigned YES unset up up 
FastEthernet1/0/23 192.168.12.25 YES NVRAM up up 
FastEthernet1/0/24 unassigned YES unset up up

\ обратный слэш служит для экранирования специальных символов (рассмотренных тут), это означает, что экранированные символы должны интерпретироваться буквально, т.е. как простые символы.

Комбинация "\$" указывает на то, что символ "$" трактуется как обычный символ, а не как признак конца строки в регулярных выражениях. Аналогично, комбинация "\\" соответствует простому символу "\".
X670-48x.1 # show lldp ports 1-48 neighbors detailed | i ": 10\."
 Management Address : 10.6.9.129
 Management Address : 10.6.10.129
 Management Address : 10.6.12.1
 Management Address : 10.6.11.129

\s – матчим пробел и спец. символы (whitespace characters)

AB\sC = "AB C"

\S – матчим все кроме whitespace characters, с помощью только \S+ можно извлекать столбцы (по примеру awk).

$ python3 ./test.py
['toster@weril.me']

import re

line = "From toster@weril.me at Sat Jan 13 01:01:01 2019"
res = re.findall("^From (\S+@\S+)", line)
print(res)

\d является сокращением для [0-9], очень круто, особенно когда цифр много, например /\d{3}/ – подпадание любых трех цифр, \d+ – подпадание всех цифр (без нулевого). Может использоваться как простейший regexp для отлова IP-адреса.

\D – любой символ, кроме цифр

\w – любая буква или цифра

\w+ # матчим слово (буквы или цифры до whitespace)

\W – все, кроме букв или цифр

 

ФЛАГИ

i/ignorecase – с помощью этого символа в конце регулярного выражения делаем его caseinsensitive, очень полезно. Причем работает даже для русских символов.

@feedback = /^(feedback|отзыв)/i

dotall– сделать так, чтобы точка срабатывала и на \n.

Конструкции

[] группа, обозначающая один символ. Используя спец. символы * и + группа может обозначать ряд символов.

  • [ ] – подпадение всего пробела
  • [^ ] – подпадение всего, кроме пробела
  • [fFkal] – подпадание любой буквы из списка
  • [^fFam] – подпадание буквы, отсутствующей в списке
  • [a-z] – подпадание любой буквы из алфавита в нижнем регистре
    • [a-zA-Z] – подпадание буквы в любом регистре
    • [а-яА-Я] – аналогично кириллица
  • [0-9] – подробнее в \d
# Пример поиска домена почты
$ python3 ./test.py
['weril.me']

line = "From toster@weril.me at Sat Jan 13 01:01:01 2019"
res = re.findall("^From .*@([^ ]*)", line)
print(res)


# Пример поиска всех цифр из строки и вычисления их суммы
$ python3 ./test.py
27486

line = '''Why should you learn to write programs? 7746
12 1929 8827
Writing programs (or programming) is a very creative 
7 and rewarding activity.  You can write programs for 
many reasons, ranging from making your living to solving
8837 a difficult data analysis problem to having fun to helping 128
someone else solve a problem.  This book assumes that 
everyone needs to know how to program ...'''
res = re.findall("[0-9]+", line)
res = map(int, res)
print(sum(res))

() – выражение в скобках рассматривается как один элемент и автоматически является группой для подпадания (matching group), позволяет получить из подпавшей под полное выражение строки конкретную группу (пример выше так же подходит).

$ python3 ./test.py
['0.432423']

import re

line = "kakayato chush = 0.432423"
res = re.findall("^kakayato chush = ([0-9.]+)", line)
print(res)

{3} – повторяем три раза предыдущий элемент или выражение в скобках (напр. () или []).

\d{8} matches exactly 8 digits. An opening curly bracket that appears in a position where a quantifier is not allowed, or one that does not match the syntax of a quantifier, is taken as a literal character. For example, {,6} is not a quantifier, but a literal string of four characters.

{3, } – матчит от трех и более повторений.

[aeiou]{3,} matches at least 3 successive vowels, but may match many more, while

z{2,4}повторения. От 2 до 4 z: матчит zz, zzz, zzzz. Если убрать четверку, но оставить запятую (применяется довольно часто) – то нет лимита сколько zz будет, главное не меньше двух. Если убрать и второе число и запятую – то матчится будет только ровно два z.

z{2,4} matches "zz", "zzz", or "zzzz". A closing brace on its own is not a special character. If the second number is omitted, but the comma is present, there is no upper limit; if the second number and the comma are both omitted, the quantifier specifies an exact number of required matches.

Leave a Reply