Pyparsing:白色空间有时很重要......有时候不会

car*_*eje 37 python pyparsing

我想为包含几个部分的文件创建一个语法(如下面的PARAGRAPH).

一个部分以其关键字(例如PARAGRAPH)开头,后跟一个标题(此处为标题),其内容在以下行中,一行内容是该部分的一行.它就像一个带有标题,列和行的表.

在下面的示例(tablefile)中,我将限制部分包含一列和一行.

Tablefile的自上而下的BNF:

tablefile := paragraph*
paragraph := PARAGRAPH title CR
             TAB content
title, content := \w+
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

应用于示例

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

PARAGRAPH someTitle
          thisIsContent
Run Code Online (Sandbox Code Playgroud)

另外这个:

PARAGRAPH someTitle
          thisIsContent
PARAGRAPH otherTitle
          thisIsOtherContent
Run Code Online (Sandbox Code Playgroud)

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

此外,我想允许选项卡和空格在文件中的任何位置没有干扰.唯一需要的行为是启动一个段落内容TAB,并PARAGRAPH开始该行.这也意味着在段落中和段落之间跳过空白行(带有制表符和空格或没有).

因此我添加了这一行:

tablefile.ignore(LineStart() + ZeroOrMore(White(' \t')) + LineEnd())
Run Code Online (Sandbox Code Playgroud)

但是我刚刚曝光的每一个要求似乎都违背了我设置默认空格的需要,' '并让我陷入了死胡同.

实际上,这会导致一切崩溃:

tablefile.ignore(CR)
tablefile.ignore(TAB)
Run Code Online (Sandbox Code Playgroud)

将PARAGRAPH和TAB粘贴到行首

如果我想在文本中的任何地方\t被忽略, wherever in the text but at the start of lines. I will have to add them to the default white space characters.

Thus, I have found a way to forbid every white space character at the start of the line. By using 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)
Run Code Online (Sandbox Code Playgroud)

有了这个解决方案,我在文本中的任何地方都用TAB解决了我的问题.

分开段落

delimitedList经过一番思考后,我达到了PaulMcGuire()的解决方案.我遇到了一些问题.

实际上,这里有两种不同的方式来声明两段之间的换行符分隔符.在我看来,它们应该是等价的.在实践中,他们不是?

崩溃测试(如果你运行它,不要忘记用标签更改空格):

PARAGRAPH titleone
          content1
PARAGRAPH titletwo
          content2
Run Code Online (Sandbox Code Playgroud)

两个例子之间的共同点:

ParserElement.setDefaultWhitespaceChars('\t ')
SOL = LineStart().suppress()
EOL = LineEnd().suppress()

title = Word()
content = Word()
PARAGRAPH = Keyword('PARAGRAPH').leaveWhitespace()
TAB = Literal('\t').leaveWhitespace()
Run Code Online (Sandbox Code Playgroud)

第一个例子,工作一个:

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

tablefile = ZeroOrMore(paragraph)
Run Code Online (Sandbox Code Playgroud)

第二个例子,不工作:

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

tablefile = delimitedList(paragraph, delim=EOL)
Run Code Online (Sandbox Code Playgroud)

它们不应该是等价的吗?第二次提出异常:

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)
Run Code Online (Sandbox Code Playgroud)

最后,文件每行可以包含一个单词,任何地方都有空格.它应该忽略空白行.

幸运的是,确实如此.但它不受默认空格声明的影响.包含空格或制表符的空行将导致解析器引发解析异常.

这种行为绝对不是我所期望的.是指定的吗?这个简单的尝试有没有错误?

我可以在这个帖子中看到PaulMcGuire并没有试图忽略空行,而是在类似makefile的语法分析器(NL = LineEnd().suppress())中对它们进行标记化.

任何用于自定义BNF解析器的python模块?

makefile_parser = ZeroOrMore( symbol_assignment
                             | task_definition
                             | NL )
Run Code Online (Sandbox Code Playgroud)

我现在唯一的解决方案是预处理文件并删除空白行中包含的空格,因为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)
Run Code Online (Sandbox Code Playgroud)

Cee*_*man 2

您的 BNF 仅包含 CR,但您解析代码以使用 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
Run Code Online (Sandbox Code Playgroud)