【问题标题】:Preserve newlines in nestedExpr在 nestedExpr 中保留换行符
【发布时间】:2017-04-16 17:24:54
【问题描述】:

nestedExpr 是否可以保留换行符?

这是一个简单的例子:

import pyparsing as pp

# Parse expressions like: \name{body}
name = pp.Word( pp.alphas )
body = pp.nestedExpr( '{', '}' )
expr = '\\' + name('name') + body('body')

# Example text to parse
txt = '''
This \works{fine}, but \it{
    does not
    preserve newlines
}
'''

# Show results
for e in expr.searchString(txt):
    print 'name: ' + e.name
    print 'body: ' + str(e.body) + '\n'

输出:

name: works
body: [['fine']]

name: it
body: [['does', 'not', 'preserve', 'newlines']]

如您所见,第二个表达式 (\it{ ...) 的主体被解析,尽管主体中有换行符,但我希望结果将每一行存储在一个单独的子数组中。这个结果使得无法区分单行与多行的正文内容。

【问题讨论】:

    标签: python newline pyparsing


    【解决方案1】:

    直到几分钟前我才看到你的答案,我已经想出了这个方法:

    body = pp.nestedExpr( '{', '}', content = (pp.LineEnd() | name.setWhitespaceChars(' ')))
    

    body 更改为此定义会得到以下结果:

    name: works
    body: [['fine']]
    
    name: it
    body: [['\n', 'does', 'not', '\n', 'preserve', 'newlines', '\n']]
    

    编辑:

    等等,如果你想要的是单独的行,那么也许这就是你要找的更多:

    single_line = pp.OneOrMore(name.setWhitespaceChars(' ')).setParseAction(' '.join)
    multi_line = pp.OneOrMore(pp.Optional(single_line) + pp.LineEnd().suppress())
    body = pp.nestedExpr( '{', '}', content = multi_line | single_line )
    

    这给出了:

    name: works
    body: [['fine']]
    
    name: it
    body: [['does not', 'preserve newlines']]
    

    【讨论】:

    • 我不认为它比包作者本人的回答更好! :) 对不起,如果我的建议有点笨拙,但我可以问这个吗?为什么在body 的定义中使用name?我承认我的问题并不完全清楚,但我真正追求的是括号之间的 raw 内容,理想情况下不受任何解析规则或标记器的影响,因此我可以稍后单独解析它们(可能然后使用不同的解析规则,具体取决于父项的内容)。
    • 要匹配 anything,您可能会使用 pp.Word(pp.printables, excludeChars="{}") 之类的东西来代替 name。您可能还必须摆弄pp.originalTextFor 的包装来获取原始字符串内容。欢迎使用 pyparsing!
    【解决方案2】:

    这个扩展(基于nestedExpr 2.1.10 版的代码)表现得更接近于我期望的“嵌套表达式”返回:

    import string
    from pyparsing import *
    
    defaultWhitechars = string.whitespace
    ParserElement.setDefaultWhitespaceChars(defaultWhitechars)
    
    def fencedExpr( opener="(", closer=")", content=None, ignoreExpr=None, stripchars=defaultWhitechars ):
    
        if content is None:
            if isinstance(opener,basestring) and isinstance(closer,basestring):
                if len(opener) == 1 and len(closer)==1:
                    if ignoreExpr is not None:
                        content = Combine(OneOrMore( ~ignoreExpr + CharsNotIn(opener+closer,exact=1)))
                    else:
                        content = empty.copy() + CharsNotIn(opener+closer)
                else:
                    if ignoreExpr is not None:
                        content = OneOrMore( ~ignoreExpr + ~Literal(opener) + ~Literal(closer))
                    else:
                        content = OneOrMore( ~Literal(opener) + ~Literal(closer) )
            else:
                raise ValueError("opening and closing arguments must be strings if no content expression is given")
    
        if stripchars is not None:
            content.setParseAction(lambda t:t[0].strip(stripchars))
    
        ret = Forward()
        if ignoreExpr is not None:
            ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) )
        else:
            ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content )  + Suppress(closer) )
        ret.setName('nested %s%s expression' % (opener,closer))
        return ret
    

    恕我直言,它修复了一些问题:

    1. 原实现在默认content中使用ParserElement.DEFAULT_WHITE_CHARS,看起来是出于懒惰;它只在ParserElement类本身之外使用了五次,其中四次在函数nestedExpr中(另一种用法在LineEnd中,它手动删除了\n)。将命名参数添加到 nestedExpr 会很容易,但公平地说,我们也可以使用 ParserElement.setDefaultWhitespaceChars 来实现相同的目的。

    2. 第二个问题是,默认情况下,content 表达式本身中的空白字符会被忽略,并带有附加的解析操作 lambda t:t[0].strip(),其中无需输入即可调用 strip,这意味着它是 removes all unicode whitespace characters。我个人认为,不要忽略内容中的任何空格,而是在结果中选择性地去除它们更有意义。出于这个原因,我在原始实现中删除了带有CharsNotIn 的标记,并引入了默认为string.whitespace 的参数stripchars

    当然,乐于接受任何建设性的批评。

    【讨论】:

    • 感谢您努力编写一些工作补丁代码 - 我通常会收到有关 应该对 pyparsing 进行更改的建议,但很少得到具体的代码补丁/实现。我认为您对nestedExpr 的解释与我的有点不同,我尝试通过支持content 参数来适应不同的嵌套规则,默认值为0 个或多个空格分隔的单词。如果给出了content 表达式,我可能需要删除该 auto-strip() 解析操作,并让调用者在给定的 arg 上设置必要的 strip 或 join 或任何解析操作。
    猜你喜欢
    • 2013-07-27
    • 2013-11-10
    • 2015-08-16
    • 2012-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-21
    相关资源
    最近更新 更多