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