Pyparsing:空格有时很重要,有时不重要

我想为包含多个部分的文件(如下面的PARAGRAPH)创build一个语法。

一节以其关键字(例如PARAGRAPH)开头,后面跟着一个标题(标题在这里),其内容在下面的行上,一行内容是该行的一行。 就像它是一个表头,行和列。

在下面的例子(tablefile)中,我将限制部分有一列和一行。

表格文件自上而下的BNF:

tablefile := paragraph* paragraph := PARAGRAPH title CR TAB content title, content := \w+ 

Pyparsing语法:

因为我需要换行和制表符来处理,所以我需要设置默认的空格为“”。

 def grammar(): ''' Bottom-up grammar definition ''' ParserElement.setDefaultWhitespaceChars(' ') TAB = White("\t").suppress() CR = LineEnd().setName("Carriage Return").suppress() PARAGRAPH = 'PARAGRAPH' title = Word(alphas) content = Word(alphas) paragraph = (PARAGRAPH + title + CR + TAB + content) tablefile = OneOrMore(paragraph) tablefile.parseWithTabs() return tablefile 

应用于例子

这个虚拟例子很容易匹配:

 PARAGRAPH someTitle thisIsContent 

这另一个less:

 PARAGRAPH someTitle thisIsContent PARAGRAPH otherTitle thisIsOtherContent 

它在第一个内容之后等待PARAGRAPH ,并偶然发现一个换行符(记住setDefaultWhitespaceChars(' ') )。 我是否被迫添加CR?paragraph的结尾? 忽略这样的最后一个换行符会是更好的方法吗?

此外,我想允许标签和空格在文件中的任何地方,而不受干扰。 唯一需要的行为是用TAB启动段落内容, PARAGRAPH开始行。 这也意味着在段落之间和段落之间跳过空行(带有制表符和空格或空白)。

因此我添加了这一行:

 tablefile.ignore(LineStart() + ZeroOrMore(White(' \t')) + LineEnd()) 

但是,我刚才暴露的每一个需求,似乎都违背了我设定默认空白的需要,并使我陷入死胡同。

事实上,这将导致一切破裂:

 tablefile.ignore(CR) tablefile.ignore(TAB) 

将PARAGRAPH和TAB粘贴到行首

如果我不想被忽视 无论在文本中,而是在行的开头。 我将不得不将它们添加到默认的空白字符。

因此,我find了一种方法来禁止每一个空白字符在行首。 通过使用leaveWhitespace方法。 该方法在匹配令牌之前保留它遇到的空白。 因此,我可以将一些标记粘贴到行首。

 ParserElement.setDefaultWhitespaceChars('\t ') SOL = LineStart().suppress() EOL = LineEnd().suppress() title = Word() content = Word() PARAGRAPH = Keyword('PARAGRAPH').leaveWhitespace() TAB = Literal('\t').leaveWhitespace() paragraph = (SOL + PARAGRAPH + title + EOL + SOL + TAB + content + EOL) 

有了这个解决scheme,我解决了我在TAB中遇到的问题。

分隔段落

经过一番思考,我达到了PaulMcGuire( delimitedList )的解决scheme。 我遇到了一些问题。

的确,这里有两种不同的方法来声明两个段落之间的换行符分隔符。 在我看来,他们应该是相当的。 在实践中,他们不是?

碰撞testing(如果你运行它,不要忘记用标签改变空格):

 PARAGRAPH titleone content1 PARAGRAPH titletwo content2 

两个例子之间的共同部分:

 ParserElement.setDefaultWhitespaceChars('\t ') SOL = LineStart().suppress() EOL = LineEnd().suppress() title = Word() content = Word() PARAGRAPH = Keyword('PARAGRAPH').leaveWhitespace() TAB = Literal('\t').leaveWhitespace() 

第一个例子,工作一:

 paragraph = (SOL + PARAGRAPH + title + EOL + SOL + TAB + content + EOL) tablefile = ZeroOrMore(paragraph) 

第二个例子,不工作:

 paragraph = (SOL + PARAGRAPH + title + EOL + SOL + TAB + content) tablefile = delimitedList(paragraph, delim=EOL) 

他们不应该相当吗? 第二个例外:

Expected end of text (at char 66), (line:4, col:1)

这对我来说并不是一个大问题,因为我终于可以放弃将EOL放到每一段段落的末尾。 但我想强调这一点。

忽略包含空格的空白行

我的另一个要求是忽略包含空白( ' \t' )的空白行。

一个简单的语法是:

 ParserElement.setDefaultWhitespaceChars(' \t') SOL = LineStart().suppress() EOL = LineEnd().suppress() word = Word('a') entry = SOL + word + EOL grammar = ZeroOrMore(entry) grammar.ignore(SOL + EOL) 

最后,文件每行可以包含一个字,任何空白都可以。 它应该忽略空行。

令人高兴的是,它确实如此。 但是它不受默认空格声明的影响。 包含空格或制表符的空白行将导致parsing器引发parsingexception。

这种行为绝对不是我所期待的。 这是指定的吗? 在这个简单的尝试下有没有一个错误?

我可以在这个线程中看到,PaulMcGuire没有试图忽略空行,而是使用类似makefile的文法分析器( NL = LineEnd().suppress() )来标记它们。

任何python模块定制BNF分析器?

 makefile_parser = ZeroOrMore( symbol_assignment | task_definition | NL ) 

我现在唯一的解决scheme是预处理文件并删除空白行中包含的空格,因为pyparsing正确地忽略了空白行中没有空格的行。

 import os preprocessed_file = os.tmpfile() with open(filename, 'r') as file: for line in file: # Use rstrip to preserve heading TAB at start of a paragraph line preprocessed_file.write(line.rstrip() + '\n') preprocessed_file.seek(0) grammar.parseFile(preprocessed_file, parseAll=True) 

你的BNF只包含CR,但你parsing代码来终止使用LF。 这是打算? BNF支持 LF(Unix),CR(Mac)和CRLF(Win)EOL:

 Rule_|_Def.__|_Meaning___ CR | %x0D | carriage return LF | %x0A | linefeed CRLF | CR LF | Internet standard newline 
    Interesting Posts