Как написать грамматику для этого в pyparsing:сопоставьте набор слов, но не содержащий заданного шаблона
Вопрос
Я новичок в Python и pyparsing.Мне нужно выполнить следующее.
Мой пример строки текста выглядит следующим образом:
12 items - Ironing Service 11 Mar 2009 to 10 Apr 2009
Washing service (3 Shirt) 23 Mar 2009
Мне нужно извлечь описание товара, точка
tok_date_in_ddmmmyyyy = Combine(Word(nums,min=1,max=2)+ " " + Word(alphas, exact=3) + " " + Word(nums,exact=4))
tok_period = Combine((tok_date_in_ddmmmyyyy + " to " + tok_date_in_ddmmmyyyy)|tok_date_in_ddmmmyyyy)
tok_desc = Word(alphanums+"-()") but stop before tok_period
Как это сделать?
Решение
Я бы посоветовал взглянуть на SkipTo как на класс pyparsing, который наиболее подходит, поскольку у вас есть хорошее определение текста нежелательных , но вы примете почти все до этого. Вот несколько способов использовать SkipTo:
text = """\
12 items - Ironing Service 11 Mar 2009 to 10 Apr 2009
Washing service (3 Shirt) 23 Mar 2009"""
# using tok_period as defined in the OP
# parse each line separately
for tx in text.splitlines():
print SkipTo(tok_period).parseString(tx)[0]
# or have pyparsing search through the whole input string using searchString
for [[td,_]] in SkipTo(tok_period,include=True).searchString(text):
print td
Оба цикла for
выводят следующее:
12 items - Ironing Service
Washing service (3 Shirt)
Другие советы
М. К. Сараванан, эту конкретную проблему синтаксического анализа не так уж сложно решить с помощью good 'ole re:
import re
import string
text='''
12 items - Ironing Service 11 Mar 2009 to 10 Apr 2009
Washing service (3 Shirt) 23 Mar 2009
This line does not match
'''
date_pat=re.compile(
r'(\d{1,2}\s+[a-zA-Z]{3}\s+\d{4}(?:\s+to\s+\d{1,2}\s+[a-zA-Z]{3}\s+\d{4})?)')
for line in text.splitlines():
if line:
try:
description,period=map(string.strip,date_pat.split(line)[:2])
print((description,period))
except ValueError:
# The line does not match
pass
урожайность
# ('12 items - Ironing Service', '11 Mar 2009 to 10 Apr 2009')
# ('Washing service (3 Shirt)', '23 Mar 2009')
Главной рабочей лошадкой здесь, конечно же, является шаблон re.Давайте разберем это на части:
\d{1,2}\s+[a-zA-Z]{3}\s+\d{4}
является ли регулярное выражение для даты эквивалентом tok_date_in_ddmmmyyyy
. \d{1,2}
соответствует одной или двум цифрам, \s+
соответствует одному или нескольким пробелам, [a-zA-Z]{3}
соответствует 3 буквам и т.д.
(?:\s+to\s+\d{1,2}\s+[a-zA-Z]{3}\s+\d{4})?
является регулярным выражением, окруженным (?:...)
.Это указывает на негруппирующееся регулярное выражение.Используя это, никакая группа (например,match.group(2)) присваивается этому регулярному выражению.Это важно, потому что date_pat.split() возвращает список, в котором каждая группа является членом списка.Подавляя группировку, мы сохраняем весь период 11 Mar 2009 to 10 Apr 2009
вместе.Вопросительный знак в конце указывает на то, что этот шаблон может встречаться ноль или один раз.Это позволяет регулярному выражению соответствовать обоим
23 Mar 2009
и 11 Mar 2009 to 10 Apr 2009
.
text.splitlines()
разбивает текст на \n
.
date_pat.split('12 items - Ironing Service 11 Mar 2009 to 10 Apr 2009')
разбивает строку на регулярное выражение date_pat.Совпадение включено в возвращенный список.Таким образом, мы получаем:
['12 items - Ironing Service ', '11 Mar 2009 to 10 Apr 2009', '']
map(string.strip,date_pat.split(line)[:2])
улучшает результат.
Если line
не соответствует date_pat
, тогда date_pat.split(line)
ВОЗВРАТ [line,]
,
итак
description,period=map(string.strip,date_pat.split(line)[:2])
вызывает ValueError, потому что мы не можем распаковать список только с одним элементом в 2-кортеж.Мы перехватываем это исключение, но просто переходим к следующей строке.