Небольшой трик с регулярными выражениями который редко вижу в чужом коде.
Допустим, вам нужно распарсить простой текст и вытащить оттуда пары имя+телефон. Вернуть всё это надо в виде списка словарей. Возьмем очень простой пример текста.
Допустим, вам нужно распарсить простой текст и вытащить оттуда пары имя+телефон. Вернуть всё это надо в виде списка словарей. Возьмем очень простой пример текста.
>>> text = '''
>>> Alex:8999123456
>>> Mike:+799987654
>>> Oleg:+344456789
>>> '''
Соответственно, для выделения нужных элементов будем использовать группы. Получится такой паттерн:(\w+):([\d+]+)
Как мы будем формировать словарь из найденных групп?>>> import re
>>> results = []
>>> for match in re.finditer(r"(\w+):([\d+]+)", text):
>>> results.append({
>>> "name": match.group(1),
>>> "phone": match.group(2)
>>> })
>>> print(results)
[{'name': 'Alex', 'phone': '8999123456'}, ...]
Можно немного сократить запись используя zip>>> results = []
>>> for match in re.finditer(r"(\w+):([\d+]+)", text):
>>> results.append(dict(zip(['name', 'phone'], match.groups())))
Но есть способ лучше! Это именованные группы в regex. Можно в паттерне указать имя группы и результат сразу забрать в виде словаря.>>> for match in re.finditer(r"(?P<name>\w+):(?P<phone>[\d+]+)", text):
>>> results.append(match.groupdict())
То есть всё что я сделал, это добавил в начале группы (внутри сбокочек) такую запись:(?P<group-name>...)
Теперь найденная группа имеет имя и можно обратиться к ней как к элементу списка>>> name = match['name']
Либо забрать сразу весь словарь методом groupdict()>>> match.groupdict()
#tricks #regexРегулярные выражения иногда могут быть просто монструозными. Выглядеть это может крайне запутанно. Сами регэкспы и без того история непростая, а когда это длинный паттерн на несколько десятков знаков, разобрать там что-либо становится не просто.
Но на помощь приходит Python и его стремление сделать нашу жизнь проще!
В функциях регулярок можно после паттерна указывать флаги, один из которых позволяет писать паттерны более свободно. А именно, добавлять пробелы и переносы, которые будут игнорированы. В результате мы можем разбить паттерн на строки и добавить комментов.
Чтобы это сработало нужно добавить флаг
Согласитесь, что даже с именованными группами а таком виде регэкспа выглядит вполне сносно 😉.
#tricks #regex
Но на помощь приходит Python и его стремление сделать нашу жизнь проще!
В функциях регулярок можно после паттерна указывать флаги, один из которых позволяет писать паттерны более свободно. А именно, добавлять пробелы и переносы, которые будут игнорированы. В результате мы можем разбить паттерн на строки и добавить комментов.
Чтобы это сработало нужно добавить флаг
re.VERBOSE. Пробелы в паттерне теперь следует указывать явно спец символами.Согласитесь, что даже с именованными группами а таком виде регэкспа выглядит вполне сносно 😉.
#tricks #regex
Ранее я делал серию постов про битовые операторы.
Вот вам ещё один наглядный пример как это используется в Python в модуле
Чтобы указать флаг для компилятора нам надо указать его после передаваемой строки. Например, добавляем флаг для игнорирования переноса строки.
Не удивительно, степени двойки. Почему? Потому что каждое следующее значение это сдвиг единицы влево.
В общем, это пример применения побитовых операций в самом Python.
Теперь вы знаете Python еще немного лучше)
#tricks #regex #libs
Вот вам ещё один наглядный пример как это используется в Python в модуле
re.Чтобы указать флаг для компилятора нам надо указать его после передаваемой строки. Например, добавляем флаг для игнорирования переноса строки.
pattern = re.compile(r"(\w+)+")
words = pattern.search(text, re.DOTALL)
А как указать несколько флагов? Ведь явно будут ситуации когда нам потребуется больше одного. Кто читал посты по битовые операторы уже понял как.pattern.search(text, re.DOTALL | re.VERBOSE)
А теперь смотрим исходники, что находится в этих атрибутах?Не удивительно, степени двойки. Почему? Потому что каждое следующее значение это сдвиг единицы влево.
>>> for n in [1, 2, 4, 8, 16, 32, 64, 128, 256]:
>>>
print(bin(n))
0b1
0b10
0b100
0b1000
0b10000
0b100000
0b1000000
0b10000000
0b100000000
Чтобы было понятней, давайте напишем тоже самое но иначе, добавим ведущие нули:000000001
000000010
000000100
000001000
000010000
000100000
001000000
010000000
100000000
Не понятно что тут происходит? Читай три поста про битовые операторы начиная с этого ➡️ https://t.iss.one/pythonotes/45В общем, это пример применения побитовых операций в самом Python.
Теперь вы знаете Python еще немного лучше)
#tricks #regex #libs
GitHub
cpython/sre_constants.py at main · python/cpython
The Python programming language. Contribute to python/cpython development by creating an account on GitHub.
Функция sub в regex может принимать функцию в качестве аргумента repl.
📄 Из документации:
If repl is a function, it is called for every non-overlapping occurrence of pattern. The function takes a single match object argument, and returns the replacement string.
То есть для каждого совпадения будет вызвана функция для вычисления замены вместо замены на одну и ту же строку для всех совпадений.
Иными словами, для замены разных совпадений на разные строки не потребуется запускать re.sub() много раз для каждой строки замены. Достаточно определить функцию, которая вернёт строку для каждого из совпадений.
Описание слишком запутанное🤔, давайте лучше рассмотрим на простом примере:
Создаем карту замены. То есть какие строки на какие требуется менять.
Используя данные этого объекта мы вычисляем замену on-the-fly!
(Данный паттерн ищет отдельные слова в тексте)
#libs #regex
📄 Из документации:
If repl is a function, it is called for every non-overlapping occurrence of pattern. The function takes a single match object argument, and returns the replacement string.
То есть для каждого совпадения будет вызвана функция для вычисления замены вместо замены на одну и ту же строку для всех совпадений.
Иными словами, для замены разных совпадений на разные строки не потребуется запускать re.sub() много раз для каждой строки замены. Достаточно определить функцию, которая вернёт строку для каждого из совпадений.
Описание слишком запутанное🤔, давайте лучше рассмотрим на простом примере:
Создаем карту замены. То есть какие строки на какие требуется менять.
remap = {
'раз': '1',
'два': '2',
'три': '3',
'четыре': '4',
'пять': '5',
}
Пишем функцию поиска строки для замены. Единственным аргументом будет объект re.Match.Используя данные этого объекта мы вычисляем замену on-the-fly!
def get_str(match: re.Match):Пример текста.
word = match.group(1)
return remap.get(word.lower()) or word
text = '''Раз Два Три Четыре ПятьТеперь запускаем re.sub и вместо строки замены (repl) подаём имя функции.
Вместе будем мы считать
Пять Четыре Три Два Раз
Мы считать научим вас
'''
(Данный паттерн ищет отдельные слова в тексте)
>>> print(re.sub(r'(\w+)', get_str, text))Думаю, достаточно наглядно 🤓
1 2 3 4 5
Вместе будем мы считать
5 4 3 2 1
Мы считать научим вас
#libs #regex