Вопрос

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

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. Как определить, что именно анализирует строка.«80001» анализируется до «почтового индекса», но как мне определить это с помощью pyparsing?Я не анализирую строку на предмет ее содержимого, а просто определяю, что это за запрос.

Это было полезно?

Решение

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

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 результата для вызова другой функции, вы можете сделать это во время синтаксического анализа, прикрепив действие синтаксического анализа к выражениям 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), и вы составляете свой парсер, комбинируя их с помощью '+', '|', '^', '~', '@' и Операторы '*'.Например, если вы хотите проанализировать номер социального страхования США, но не использовать регулярное выражение, вы можете использовать:

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

Слово соответствует смежным «словам», состоящим из заданных символов в его конструкторе. Функция «Объединить» объединяет совпадающие токены в один токен.

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

delimitedList(ssn, '/')

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

ssn * (1,3)

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

Когда я писал pyparsing, я хотел, чтобы эта композиция парсеров из основных строительных блоков стала основной формой для создания парсера.И только в более поздней версии я добавил 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']

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

Другие советы

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

zipresults = zipcode.parseString( t )
fooresults = foo.parseString( t )

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top