2014-11-22 2 views
4

Я пытаюсь разбить строку, как aaa:bbb(123), в токены, используя Pyparsing.Жадные выражения в Pyparsing

Я могу сделать это с регулярным выражением, но мне нужно сделать это через Pyparsing.

С re решение будет выглядеть следующим образом:

>>> import re 
>>> string = 'aaa:bbb(123)' 
>>> regex = '(\S+):(\S+)\((\d+)\)' 
>>> re.match(regex, string).groups() 
('aaa', 'bbb', '123') 

Это достаточно ясно и просто. Ключевым моментом здесь является \S+, что означает «все, кроме белых».

Теперь я постараюсь сделать это с Pyparsing:

>>> from pyparsing import Word, Suppress, nums, printables 
>>> expr = (
...  Word(printables, excludeChars=':') 
...  + Suppress(':') 
...  + Word(printables, excludeChars='(') 
...  + Suppress('(') 
...  + Word(nums) 
...  + Suppress(')') 
...) 
>>> expr.parseString(string).asList() 
['aaa', 'bbb', '123'] 

Хорошо, мы получили тот же результат, но это не выглядит хорошо. Мы установили excludeChars, чтобы выражения Pyparsing остановились там, где нам нужно, но это не выглядит надежным. Если мы будем иметь «исключенные» символов в исходной строке, то же регулярное выражение будет работать нормально:

>>> string = 'a:aa:b(bb(123)' 
>>> re.match(regex, string).groups() 
('a:aa', 'b(bb', '123') 

в то время как Pyparsing исключение, очевидно, перерыв:

>>> expr.parseString(string).asList() 
Traceback (most recent call last): 
    File "<input>", line 1, in <module> 
    File "/long/path/to/pyparsing.py", line 1111, in parseString 
    raise exc 
ParseException: Expected W:(0123...) (at char 7), (line:1, col:8) 

Итак, вопрос является как мы можем реализовать нужна ли логика с помощью Pyparsing?

ответ

1

Используйте регулярное выражение с опережением утверждением:

from pyparsing import Word, Suppress, Regex, nums, printables 

expr = (
    Word(printables, excludeChars=':') 
    + Suppress(':') 
    + Regex(r'\S+[^\(](?=\()') 
    + Suppress('(') 
    + Word(nums) 
    + Suppress(')') 
) 
1

В отличии от регулярных выражений, Pyparsing чисто слева направо ищет, без неявного опережающего просмотра.

Если вы хотите предпросмотр и откаты регулярного выражения, вы могли бы просто использовать Regex, содержащий исходный ре:

expr = Regex(r"(\S+):(\S+)\((\d+)\)") 
print expr.parseString(string).dump() 

['aaa:b(bb(123)'] 

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

expr = Regex(r"(?P<field1>\S+):(?P<field2>\S+)\((?P<field3>\d+)\)") 
print expr.parseString(string).dump() 

['aaa:b(bb(123)'] 
- field1: aaa 
- field2: b(bb 
- field3: 123  

Это говорит мне, что хорошее улучшение было бы добавить конструктор ARG в Regex вернуть результаты как список всех групп re, а не строки.

Смежные вопросы