2017-04-16 79 views
2

nestedExpr可能會保留換行符嗎?在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{ ...)被解析儘管在體內的新行,但我會預期結果將每行存儲在一個單獨的子陣列中。這個結果使得不可能用單行與多行來區分主體內容。

回答

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']] 
+0

我不認爲它比包裝本身作者的回答更好! :)對不起,如果我的建議有點笨拙,但我可以問在這一個;爲什麼你在'body'的定義中使用'name'?我承認我的問題並不完全清楚,但我真正追求的是方括號之間的_raw_內容,理想情況下不受任何解析規則或標記器的影響,所以我可以稍後分別解析它們(可能隨後使用不同的解析規則,具體取決於關於父母的內容)。 – Sheljohn

+1

爲了匹配任何*,代替'name',你可能會使用類似'pp.Word(pp.printables,excludeChars =「{}」)''的東西。您可能還必須用'pp.originalTextFor'來包裝以獲取原始字符串內容。歡迎來到pyparsing! – PaulMcG

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 

恕我直言,它修復了幾件事情:

  1. 最初的實現使用ParserElement.DEFAULT_WHITE_CHARS在默認content,這似乎是懶惰;它只在ParserElement類別本身以外使用五次,其中四個在功能nestedExpr(其他用法在LineEnd中,並且它手動刪除\n)。取而代之的是向nestedExpr添加一個命名參數將很容易,儘管公平我們也可以使用ParserElement.setDefaultWhitespaceChars來實現相同的目的。

  2. 第二個問題是,默認情況下,空白字符被在content表達式本身忽略,與附加解析動作lambda t:t[0].strip(),其中條帶被稱爲無輸入,這意味着它removes all unicode whitespace characters。我個人認爲不要忽略內容中的任何空格,而是在結果中選擇性地去除它們。出於這個原因,我在原始實現中刪除了帶有CharsNotIn的令牌,並引入了參數stripchars,默認爲string.whitespace

很高興對此採取任何建設性的批評。

+1

感謝您使工作了一些工作補丁代碼的努力 - 我通常會在變化的建議* I *應作出pyparsing,但只有很少得到具體的代碼補丁/實現。我認爲你對'nestedExpr'的解釋有點不同,我試圖通過支持'content'參數來適應不同的嵌套規則,默認情況下是0或更多空白分隔的單詞。我可能需要刪除auto-strip()分析動作,雖然給出了'content'表達式,並讓調用者在給定的arg上設置必要的strip或join或任何分析動作。 – PaulMcG