Используйте эту библиотеку синтаксического анализа текста Python, чтобы попрощаться с «обычными»

Python

Эта статья выбрана из моей личной электронной книги»Справочник по черной магии 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. Результат разбора

Разбирайте результаты только двух результатов:

  1. Совпадения нет, значение parse равно None
>>> parse("halo", "hello") is None
True
>>>
  1. Если есть совпадение, значением 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 для написания обычной разработки.Написанный с его помощью код красив и удобочитаем, и нет необходимости поддерживать код позже.

Как реализован нижний слой, зависит ли это от регулярности, мы не знаем.

Теста на скорость нет, а визуальный осмотр не будет быстрее штатного. Однако сам парсинг подходит для особых случаев: он подходит для парсинга небольших текстов с указанием формата, если вы хотите парсить большие тексты, рекомендуется запускать их регулярно.