вопрос с разбором - PullRequest
       44

вопрос с разбором

1 голос
/ 06 февраля 2010

Этот код работает:

from pyparsing import *

zipRE = "\d{5}(?:[-\s]\d{4})?" 
fooRE = "^\!\s+.*"

zipcode = Regex( zipRE )
foo = Regex( fooRE )

query = ( zipcode | foo )



tests = [ "80517", "C6H5OH", "90001-3234", "! sfs" ]

for t in tests:
    try:
        results = query.parseString( t )
        print t,"->", results
    except ParseException, pe:
        print pe

Я застрял в двух вопросах:

1 - Как использовать пользовательскую функцию для разбора токена. Например, если бы я хотел использовать некоторую пользовательскую логику вместо регулярного выражения, чтобы определить, является ли число почтовым индексом. Вместо:

zipcode = Regex( zipRE )

возможно:

zipcode = MyFunc()

2 - Как определить, что строка анализирует в TO. «80001» анализирует «почтовый индекс», но как мне определить это с помощью pyparsing? Я не анализирую строку для ее содержимого, а просто определяю, какой это запрос.

Ответы [ 3 ]

3 голосов
/ 06 февраля 2010

Вы можете использовать zipcode и foo по отдельности, чтобы вы знали, какой из этих строк соответствует.

zipresults = zipcode.parseString( t )
fooresults = foo.parseString( t )
2 голосов
/ 06 февраля 2010

Ваш второй вопрос прост, поэтому я отвечу на него первым. Измените запрос, чтобы присвоить имена результатов различным выражениям:

query = ( zipcode("zip") | foo("foo") ) 

Теперь вы можете вызвать getName () для возвращенного результата:

print t,"->", results, results.getName()

Предоставление:

80517 -> ['80517'] zip
Expected Re:('\\d{5}(?:[-\\s]\\d{4})?') (at char 0), (line:1, col:1)
90001-3234 -> ['90001-3234'] zip
! sfs -> ['! sfs'] foo

Если вы собираетесь использовать результат fooness или zipness для вызова другой функции, то вы можете сделать это во время разбора, прикрепив действие parse к выражениям foo и zipcode:

# enclose zipcodes in '*'s, foos in '#'s
zipcode.setParseAction(lambda t: '*' + t[0] + '*')
foo.setParseAction(lambda t: '#' + t[0] + '#')

query = ( zipcode("zip") | foo("foo") ) 

Теперь дает:

80517 -> ['*80517*'] zip
Expected Re:('\\d{5}(?:[-\\s]\\d{4})?') (at char 0), (line:1, col:1)
90001-3234 -> ['*90001-3234*'] zip
! sfs -> ['#! sfs#'] foo

Что касается вашего первого вопроса, я точно не знаю, какую функцию вы имеете в виду. Pyparsing предоставляет гораздо больше классов синтаксического анализа, чем просто Regex (например, Word, Keyword, Literal, CaselessLiteral), и вы создаете свой синтаксический анализатор, комбинируя их с '+', '|', '^', '~', '@' и операторы *. Например, если вы хотите проанализировать номер социального страхования США, но не используете Regex, вы можете использовать:

ssn = Combine(Word(nums,exact=3) + '-' + 
        Word(nums,exact=2) + '-' + Word(nums,exact=4))

Слово соответствует непрерывным «словам», состоящим из заданных символов в конструкторе, Combine объединяет совпавшие токены в один токен.

Если вы хотите проанализировать потенциальный список таких чисел, разделенных символами /, используйте:

delimitedList(ssn, '/')

или если было от 1 до 3 таких чисел без разделителей, используйте:

ssn * (1,3)

И к любому выражению могут быть прикреплены имена результатов или действия синтаксического анализа для дополнительного обогащения анализируемых результатов или функциональности во время синтаксического анализа. Вы даже можете создавать рекурсивные парсеры, такие как вложенные списки скобок, арифметические выражения и т. Д., Используя класс Forward.

Моим намерением, когда я писал pyparsing, было то, что эта композиция парсеров из базовых строительных блоков будет основной формой для создания парсера. Только в более позднем выпуске я добавил Regex как (хотя я и был) конечный аварийный клапан - если люди не могут создать свой парсер, они могут использовать формат regex, который определенно доказал свою силу с течением времени.

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

class PairOf(Token):
    """Token for matching words composed of a pair
       of characters in a given set.
    """
    def __init__( self, chars ):
        super(PairOf,self).__init__()
        self.pair_chars = set(chars)

    def parseImpl( self, instring, loc, doActions=True ):
        if (loc < len(instring)-1 and 
           instring[loc] in self.pair_chars and
           instring[loc+1] == instring[loc]):
            return loc+2, instring[loc:loc+2]
        else:
            raise ParseException(instring, loc, "Not at a pair of characters")

Так что:

punc = r"~!@#$%^&*_-+=|\?/"
parser = OneOrMore(Word(alphas) | PairOf(punc))
print parser.parseString("Does ** this match @@@@ %% the parser?")

Дает:

['Does', '**', 'this', 'match', '@@', '@@', '%%', 'the', 'parser']

(Обратите внимание на пропущение завершающего сингла '?')

2 голосов
/ 06 февраля 2010

У меня нет модуля pyparsing, но Regex должен быть классом, а не функцией.

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

...