Использование pyparsing для анализа списка из строки со специальными символами

Я хотел бы проанализировать список из строкового поля, которое может содержать специальные символы, такие как \\n \r \n \t, и расшириться на несколько строк. В настоящее время мне нужно сначала проанализировать строку, очистить ее, а затем применить грамматику списка к этой чистой строке. Это работает нормально, но просто интересно, есть ли лучший способ.

Это то, что у меня сейчас есть

str_ = QuotedString('"',escChar='\\',multiline=True)  #grammar for str
str_.setParseAction(lambda pr: pr[0].replace('\\n',' ')\
                        .replace('\\r', ' ')\
                        .replace('\r', ' ')\
                        .replace('\t', ' '))

list_G = delimitedList(Word(printables))('mlist') #grammar for list


def pa(st,locn,pr): return list_G.parseString(pr.mystr)
mylist = Group(str_('mystr').addParseAction(pa)) #read in the str then re-parse
G = Keyword("LIST") + mylist('thelist') + ';'    #grammar for the whole thing
s = 'LIST "one,two,three" ;'  

Изменить: вместо Word (печатные формы) в list_G изменено на

var_grammar = Word(alphas+"_", alphanums + "_") #"_a,a2b_,.."
num_grammar = Regex(r"[+-]?\d+(:?\.\d*)?(:?[eE][+-]?\d+)?")('num')
list_G = delimitedList(var_grammar|num_grammar)('mlist') #grammar for list
G = Keyword("LIST") + '"' + mylist('thelist') + '"' + ';' 

Причина, которая заставляет меня выполнить очистку выше, которая заменяет "\\n", "\\r" with ' ', заключается в том, что я читаю строку из файла, который буквально содержит символы \n, \r, и они не поддаются разбору по var_name или num (они не для печати)

Это пример (необработанной) строки в файле:

LIST "one,two,
 three,
 \nfour,\rfive";

Есть ли у вас какие-либо предложения по этому поводу?


person Vu Nguyen    schedule 06.12.2012    source источник
comment
Можете ли вы отредактировать свой вопрос, чтобы включить некоторые специальные символы в s и показать, в чем проблема, если вы не выполняете синтаксический анализ таким двухэтапным способом?   -  person PaulMcG    schedule 07.12.2012
comment
Если у вас действительно есть символы обратной косой черты и «n» в самой строке, определите выражение, например unwanted = Word('\\','rnt',exact=2), а затем выполните G.ignore(unwanted).   -  person PaulMcG    schedule 07.12.2012
comment
это кажется хорошим решением - должно быть эквивалентно текущему моему хаку, который заменяет эти \t \n \r пробелом сразу после чтения файла и перед его разбором. Спасибо   -  person Vu Nguyen    schedule 07.12.2012


Ответы (1)


Pyparsing довольно терпим к пробельным символам, как вы показываете здесь, поэтому я удивлен, что это проблема.

Одна проблема, которая у вас есть, связана с определением list_G:

list_G = delimitedList(Word(printables))('mlist') #grammar for list

Я понимаю, что вы пытаетесь здесь сделать, но у вашего выражения элемента списка есть ключевая проблема. delimitedList(expr) — это удобное сокращение для expr + ZeroOrMore(Suppress(',') + expr), но выражение элемента списка, которое вы используете для expr, — это Word(printables), группа слов из любого непробельного символа. К сожалению, это включает ',', разделитель вашего списка. Когда вы передаете свою строку «один, два, три» этому выражению, Word(printables) будет анализировать все это, прежде чем он когда-либо будет искать разделитель:

>>> list_G = delimitedList(Word(printables))
>>> print (list_G.parseString("one,two,three"))
['one,two,three']

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

word_of_everything_except_a_comma = Word(''.join(c for c in printables if c != ','))

В версии 1.5.6 я добавил в Word аргумент excludeChars, чтобы упростить это. Теперь вы можете просто написать:

word_of_everything_except_a_comma = Word(printables, excludeChars=',')

С таким словом вы получите правильный список из 3 элементов:

>>> list_G = delimitedList(Word(printables, excludeChars=','))
>>> print (list_G.parseString("one,two,three"))
['one', 'two', 'three']

Это может быть проблемой, с которой вы боролись, поскольку pyparsing будет неявно пропускать символы пробела, которые вы удаляете:

>>> s = "one,  two\t\t,\n\n\t\t\t  three"
>>> print (s)
one,  two               ,

                          three
>>> print (list_G.parseString(s))
['one', 'two', 'three']
person PaulMcG    schedule 07.12.2012
comment
Привет, Пол, я использовал грамматику для переменных и чисел, но решил использовать printables для поста, потому что он чище (см. отредактированный пост). Я отредактировал свой исходный пост, в котором есть пример, когда что-то не работает. Надеюсь, вы сможете взглянуть. - person Vu Nguyen; 07.12.2012