Эта статья выбрана из моей личной электронной книги»Справочник по черной магии Python》
Онлайн-чтение «Python Black Magic Handbook»:magic.iswbm.com
Адрес проекта на гитхабе:GitHub.com/is BM/магия…
Из указанной строки для получения нужных данных нормальные люди будут думать о регулярных выражениях, верно?
Любой, кто написал регулярные выражения, знает, что регулярные выражения не сложно начать и легко писать.
Тем не менее, регулярное выражение почти не читается, оно поддерживается, оно будет действительно сумасшедшим, не думайте, что это правило заключается в том, что вы можете его контролировать, вы можете не знать его через месяц.
Можно сказать, что мир давно страдает.
Сегодня я познакомлю вас с хорошей вещью, которая может избавить вас от вашего очередного кошмара, то есть с очень непопулярной библиотекой на Python — parse.
1. Реальный случай
В качестве примера возьмем недавнее реальное использование синтаксического анализа.
Ниже приведена таблица потоков ovs.Теперь мне нужно собрать и извлечь, сколько трафика и сколько пакетов проходит через эту таблицу потоков в виртуальной машине (сетевой порт). То есть значение n_bytes и n_packets, соответствующее каждому in_port.
cookie=0x9816da8e872d717d, duration=298506.364s, table=0, n_packets=480, n_bytes=20160, priority=10,arp,in_port="tapbbdf080b-c2" actions=resubmit(,24)
Если бы это были вы, что бы вы сделали?
Сначала разделены запятыми, затем разделены равными, чтобы вынуть значения?
Можете попробовать, код должен быть написан так, как я себе представлял, без всякого смысла.
Позвольте мне показать вам, как я это делаю?
Как видите, я использую сторонний пакет под названием parse, который нужно установить сам по себе.
$ python -m pip install parse
Из приведенного выше случая вы должны почувствовать, что синтаксический анализ очень эффективен для синтаксического анализа канонических строк.
2. Результат разбора
Разбирайте результаты только двух результатов:
- Совпадения нет, значение parse равно None
>>> parse("halo", "hello") is None
True
>>>
- Если есть совпадение, значением parse является экземпляр Result.
>>> parse("hello", "hello world")
>>> parse("hello", "hello")
<Result () {}>
>>>
Если вы пишете правила синтаксического анализа, которые не определяют имена полей для полей, то есть анонимные поля, Result будет экземпляром, подобным списку, как показано ниже:
>>> profile = parse("I am {}, {} years old, {}", "I am Jack, 27 years old, male")
>>> profile
<Result ('Jack', '27', 'male') {}>
>>> profile[0]
'Jack'
>>> profile[1]
'27'
>>> profile[2]
'male'
И если вы напишите правило синтаксического анализа, определяющее имя поля для поля, Result будет экземпляром, подобным словарю, как показано ниже:
>>> profile = parse("I am {name}, {age} years old, {gender}", "I am Jack, 27 years old, male")
>>> profile
<Result () {'gender': 'male', 'age': '27', 'name': 'Jack'}>
>>> profile['name']
'Jack'
>>> profile['age']
'27'
>>> profile['gender']
'male'
3. Повторное использование шаблонов
Как и в случае с re, синтаксический анализ также поддерживает повторное использование шаблонов.
>>> from parse import compile
>>>
>>> pattern = compile("I am {}, {} years old, {}")
>>> pattern.parse("I am Jack, 27 years old, male")
<Result ('Jack', '27', 'male') {}>
>>>
>>> pattern.parse("I am Tom, 26 years old, male")
<Result ('Tom', '26', 'male') {}>
4. Введите преобразование
Из приведенного выше примера вы должны заметить, что когда синтаксический анализ получает возраст, он становится"27", это строка, есть ли способ преобразовать ее в соответствии с нашим типом при извлечении?
Вы можете написать так.
>>> from parse import parse
>>> profile = parse("I am {name}, {age:d} years old, {gender}", "I am Jack, 27 years old, male")
>>> profile
<Result () {'gender': 'male', 'age': 27, 'name': 'Jack'}>
>>> type(profile["age"])
<type 'int'>
Помимо преобразования его в целое число, есть ли какой-либо другой формат?
Существует множество встроенных форматов, таких как
время матча
>>> parse('Meet at {:tg}', 'Meet at 1/2/2011 11:00 PM')
<Result (datetime.datetime(2011, 2, 1, 23, 0),) {}>
Дополнительные типы см. в официальной документации:
| Type | Characters Matched | Output |
|---|---|---|
| l | Letters (ASCII) | str |
| w | Letters, numbers and underscore | str |
| W | Not letters, numbers and underscore | str |
| s | Whitespace | str |
| S | Non-whitespace | str |
| d | Digits (effectively integer numbers) | int |
| D | Non-digit | str |
| n | Numbers with thousands separators (, or .) | int |
| % | Percentage (converted to value/100.0) | float |
| f | Fixed-point numbers | float |
| F | Decimal numbers | Decimal |
| e | Floating-point numbers with exponent e.g. 1.1e-10, NAN (all case insensitive) | float |
| g | General number format (either d, f or e) | float |
| b | Binary numbers | int |
| o | Octal numbers | int |
| x | Hexadecimal numbers (lower and upper case) | int |
| ti | Дата/время в формате ISO 8601, например, 1972-01-20T10:21:36Z (T и Z необязательно) | datetime |
| te | RFC2822 e-mail format date/time e.g. Mon, 20 Jan 1972 10:21:36 +1000 | datetime |
| tg | Global (day/month) format date/time e.g. 20/1/1972 10:21:36 AM +1:00 | datetime |
| ta | US (month/day) format date/time e.g. 1/20/1972 10:21:36 PM +10:30 | datetime |
| tc | ctime() format date/time e.g. Sun Sep 16 01:03:52 1973 | datetime |
| th | HTTP log format date/time e.g. 21/Nov/2011:00:07:11 +0000 | datetime |
| ts | Linux system log format date/time e.g. Nov 9 03:37:44 | datetime |
| tt | Time e.g. 10:21:36 PM -5:30 | time |
5. Удаляйте пробелы при извлечении
убрать пробелы с обеих сторон
>>> parse('hello {} , hello python', 'hello world , hello python')
<Result (' world ',) {}>
>>>
>>>
>>> parse('hello {:^} , hello python', 'hello world , hello python')
<Result ('world',) {}>
убрать левое пространство
>>> parse('hello {:>} , hello python', 'hello world , hello python')
<Result ('world ',) {}>
удалить правое пространство
>>> parse('hello {:<} , hello python', 'hello world , hello python')
<Result (' world',) {}>
6. Переключатель, чувствительный к регистру
Parse по умолчанию нечувствителен к регистру, вы пишете hello и HELLO одинаково.
Если вам нужна чувствительность к регистру, вы можете добавить параметр, как показано ниже:
>>> parse('SPAM', 'spam')
<Result () {}>
>>> parse('SPAM', 'spam') is None
False
>>> parse('SPAM', 'spam', case_sensitive=True) is None
True
7. Сопоставьте количество символов
Точное совпадение: укажите максимальное количество символов
>>> parse('{:.2}{:.2}', 'hello') # 字符数不符
>>>
>>> parse('{:.2}{:.2}', 'hell') # 字符数相符
<Result ('he', 'll') {}>
Нечеткое соответствие: укажите минимальное количество символов
>>> parse('{:.2}{:2}', 'hello')
<Result ('h', 'ello') {}>
>>>
>>> parse('{:2}{:2}', 'hello')
<Result ('he', 'llo') {}>
Чтобы выполнить преобразование формата в точный / нечеткий режим сопоставления, вы можете написать
>>> parse('{:2}{:2}', '1024')
<Result ('10', '24') {}>
>>>
>>>
>>> parse('{:2d}{:2d}', '1024')
<Result (10, 24) {}>
8. Три важных атрибута
В Parse есть три очень важных свойства.
- исправлено: кортеж анонимных полей извлекался с позицией
- named: словарь, в котором хранятся именованные поля
- spans: место, где хранятся совпавшие поля
Следующий код покажет вам разницу между ними
>>> profile = parse("I am {name}, {age:d} years old, {}", "I am Jack, 27 years old, male")
>>> profile.fixed
('male',)
>>> profile.named
{'age': 27, 'name': 'Jack'}
>>> profile.spans
{0: (25, 29), 'age': (11, 13), 'name': (5, 9)}
>>>
9. Преобразование пользовательских типов
Совпадающая строка будет передана в качестве параметра соответствующей функции.
Например, как мы уже говорили, преобразование строки в целое число
>>> parse("I am {:d}", "I am 27")
<Result (27,) {}>
>>> type(_[0])
<type 'int'>
>>>
что эквивалентно
>>> def myint(string):
... return int(string)
...
>>>
>>>
>>> parse("I am {:myint}", "I am 27", dict(myint=myint))
<Result (27,) {}>
>>> type(_[0])
<type 'int'>
>>>
Используя его, мы можем настроить множество функций, например, я хочу сделать совпадающую строку все в верхнем регистре
>>> def shouty(string):
... return string.upper()
...
>>> parse('{:shouty} world', 'hello world', dict(shouty=shouty))
<Result ('HELLO',) {}>
>>>
10 Подводя итог
Удобство, обеспечиваемое библиотекой синтаксического анализа в сценариях синтаксического анализа и обработки строк, заметно невооруженным глазом и просто в использовании.
В некоторых простых сценариях использование синтаксического анализа может быть на несколько уровней выше, чем использование re для написания обычной разработки.Написанный с его помощью код красив и удобочитаем, и нет необходимости поддерживать код позже.
Как реализован нижний слой, зависит ли это от регулярности, мы не знаем.
Теста на скорость нет, а визуальный осмотр не будет быстрее штатного. Однако сам парсинг подходит для особых случаев: он подходит для парсинга небольших текстов с указанием формата, если вы хотите парсить большие тексты, рекомендуется запускать их регулярно.