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{ ...
)的正文仍被解析,但我希望结果将每一行存储在一个单独的子数组中。这个结果使得无法区分身体内容与单行与多行。
答案 0 :(得分:2)
直到几分钟前我才回答你的答案,我已经提出了这个方法:
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']]
答案 1 :(得分:0)
此扩展(基于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
恕我直言,它解决了一些问题:
原始实现使用默认ParserElement.DEFAULT_WHITE_CHARS
中的content
,这似乎是出于懒惰;它只在ParserElement
类本身之外使用了五次,其中四次在函数nestedExpr
中(另一种用法在LineEnd
中,并且它手动删除\n
)。对nestedExpr
添加命名参数很容易,但为了公平起见,我们也可以使用ParserElement.setDefaultWhitespaceChars
来实现相同的目标。
第二个问题是,默认情况下,content
表达式本身会忽略空白字符,并附加解析操作lambda t:t[0].strip()
,其中调用strip而不输入,这意味着它{ {3}}。我个人认为更有意义的是不要忽略内容中的任何空格,而是在结果中有选择地去除它们。出于这个原因,我在原始实现中删除了带有CharsNotIn
的令牌,并引入了默认为stripchars
的{{1}}参数。
很高兴对此采取任何建设性的批评。