Совпадение слова, за которым следуют две группы опций в любом порядке - PullRequest
0 голосов
/ 15 января 2019

Я пишу что-то вроде парсера для маленькой библиотеки.

Моя строка имеет следующий формат:

text = "Louis,Edward,John|85.56!26,Billy,Don!18|78.0,Dean"

Просто чтобы быть более понятным, это список людей имен , разделенных запятыми и сопровождаемых двумя дополнительными разделителями ( | и ! ), после первого стоит weight , то есть number с 0-2 десятичными знаками, в то время как после "!" есть целое число, представляющее возраст. Разделители и связанные значения могут отображаться в любом порядке, как вы можете видеть для John и для Don .

Мне нужно извлечь с помощью Regex (я знаю, что мог бы сделать это многими другими способами) все имена длиной от 2 до 4 и два разделителя и следующие значения, если они присутствуют.

Это мой ожидаемый результат :

[('John', '|85.56', '!26'), ('Don', '|78.00' ,'!18'), ('Dean', '', '')]

Я пытаюсь с этим кодом:

import re
text = "Louis,Edward,John|85.56!26,Billy,Don!18|78.0,Dean"
pattern = re.compile(r'(\b\w{2,4}\b)(\!\d+)?(\|\d+(?:\.\d{1,2})?)?')
search_result = pattern.findall(text)
print(search_result)

Но это фактический результат:

[('John', '', '|85.56'), ('26', '', ''), ('Don', '!18', '|78.0'), ('Dean', '', '')]

Ответы [ 2 ]

0 голосов
/ 15 января 2019

Pyparsing хорош для составления сложных выражений из более простых и включает в себя множество встроенных функций для необязательных, неупорядоченных и разделенных запятыми значений. Смотрите комментарии в коде ниже:

import pyparsing as pp

real = pp.pyparsing_common.real
integer = pp.pyparsing_common.integer
name = pp.Word(pp.alphas, min=2, max=4)

# a valid person entry starts with a name followed by an optional !integer for age
# and an optional |real for weight; the '&' operator allows these to occur in either
# order, but at most only one of each will be allowed
expr = pp.Group(name("name") 
                + (pp.Optional(pp.Suppress('!') + integer("age"), default='')
                   & pp.Optional(pp.Suppress('|') + real("weight"), default='')))

# other entries that we don't care about
other = pp.Word(pp.alphas, min=5)

# an expression for the complete input line - delimitedList defaults to using
# commas as delimiters; and we don't really care about the other entries, just
# suppress them from the results; whitespace is also skipped implicitly, but that
# is not an issue in your given sample text
input_expr = pp.delimitedList(expr | pp.Suppress(other))

# try it against your test data
text = "Louis,Edward,John|85.56!26,Billy,Don!18|78.0,Dean"                        
input_expr.runTests(text)

Печать:

Louis,Edward,John|85.56!26,Billy,Don!18|78.0,Dean
[['John', 85.56, 26], ['Don', 18, 78.0], ['Dean', '', '']]
[0]:
  ['John', 85.56, 26]
  - age: 26
  - name: 'John'
  - weight: 85.56
[1]:
  ['Don', 18, 78.0]
  - age: 18
  - name: 'Don'
  - weight: 78.0
[2]:
  ['Dean', '', '']
  - name: 'Dean'

В этом случае использование предопределенных вещественных и целочисленных выражений не только анализирует значения, но также выполняет преобразование в int и float. Именованные параметры могут быть доступны как атрибуты объекта:

for person in input_expr.parseString(text):
    print("({!r}, {}, {})".format(person.name, person.age, person.weight))

Дает:

('John', 26, 85.56)
('Don', 18, 78.0)
('Dean', , )
0 голосов
/ 15 января 2019

Кажется, что следующее регулярное выражение дает то, что вы хотите:

re.findall(r'(\b[a-z]{2,4}\b)(?:(!\d+)|(\|\d+(?:\.\d{,2})?))*', text, re.I)
#[('John', '!26', '|85.56'), ('Don', '!18', '|78.0'), ('Dean', '', '')]

Если вам не нужны эти имена, вы можете легко отфильтровать их.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...