Как написать грамматику для этого в pyparsing: сопоставить набор слов, но не содержащий данный шаблон - PullRequest
2 голосов
/ 26 ноября 2009

Я новичок в 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

Как это сделать?

Ответы [ 2 ]

5 голосов
/ 28 ноября 2009

Я бы посоветовал взглянуть на 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) 
3 голосов
/ 27 ноября 2009

M K Сараванан, эта конкретная проблема с парсингом не так уж и сложна для хороших проблем:

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')

Основной рабочей лошадкой здесь, конечно, является повторный паттерн. Давайте разберем это на части:

\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-кортеже. Мы ловим это исключение, но просто переходим на следующую строку.

...