2013-06-06 77 views
2

我需要解析由大括號分隔信息的文件,例如:解析文件brakets

Continent 
{ 
Name Europe 
Country 
{ 
Name UK 
Dog 
{ 
Name Fiffi 
Colour Gray 
} 
Dog 
{ 
Name Smut 
Colour Black 
} 
} 
} 

以下是我在Python

from io import open 
from pyparsing import * 
import pprint 

def parse(s): 
    return nestedExpr('{','}').parseString(s).asList() 

def test(strng): 
    print strng 
    try: 
     cfgFile = file(strng) 
     cfgData = "".join(cfgFile.readlines()) 
     list = parse(cfgData) 
     pp = pprint.PrettyPrinter(2) 
     pp.pprint(list) 

    except ParseException, err: 
     print err.line 
     print " "*(err.column-1) + "^" 
     print err 

    cfgFile.close() 
    print 
    return list 

if __name__ == '__main__': 
    test('testfile') 

都試過,但這個失敗一個錯誤:

testfile 
Continent 
^ 
Expected "{" (at char 0), (line:1, col:1) 

Traceback (most recent call last): 
    File "xxx.py", line 55, in <module> 
    test('testfile') 
    File "xxx.py", line 40, in test 
    return list 
UnboundLocalError: local variable 'list' referenced before assignment 

我需要做些什麼來完成這項工作? 是否比pyparsing更好的解析器?

+2

偏離主題,但最好不要使用'list'作爲變量名,因爲它是保留在python – oleg

+0

我可以提出你的解決方案與基於遞歸的算法。它是否符合你的要求(將整個數據分割爲'{'once'和rsplit'}'一次,外部部分應該被解析爲返回字典的鍵值,而內部部分應該被傳遞給遞歸函數本身。 ) – oleg

+1

請顯示想要的結果。 –

回答

4

遞歸性是這裏的關鍵。嘗試周圍的東西:

def parse(it): 
    result = [] 
    while True: 
     try: 
      tk = next(it) 
     except StopIteration: 
      break 

     if tk == '}': 
      break 
     val = next(it) 
     if val == '{': 
      result.append((tk,parse(it))) 
     else: 
      result.append((tk, val)) 

    return result 

用例:

import pprint  

data = """ 
Continent 
{ 
Name Europe 
Country 
{ 
Name UK 
Dog 
{ 
Name Fiffi 
Colour Gray 
} 
Dog 
{ 
Name Smut 
Colour Black 
} 
} 
} 
""" 

r = parse(iter(data.split())) 
pprint.pprint(r) 

...這產生(Python 2.6中):

[('Continent', 
    [('Name', 'Europe'), 
    ('Country', 
    [('Name', 'UK'), 
    ('Dog', [('Name', 'Fiffi'), ('Colour', 'Gray')]), 
    ('Dog', [('Name', 'Smut'), ('Colour', 'Black')])])])] 

請以此爲只有起點,和感覺根據您的需要隨意改進代碼(取決於您的數據,字典可能是更好的選擇,也許)。此外,示例代碼不妥善處理病形成的數據(尤其是額外的或丟失} - 我希望你做一個全面的測試覆蓋率;)


編輯:發現pyparsing,我嘗試以下這似乎工作(多)更好,可能是(更多)爲特殊需要輕鬆進行量身定製:

import pprint 
from pyparsing import Word, Literal, Forward, Group, ZeroOrMore, alphas 

def syntax(): 
    lbr = Literal('{').suppress() 
    rbr = Literal('}').suppress() 
    key = Word(alphas) 
    atom = Word (alphas) 
    expr = Forward() 
    pair = atom | (lbr + ZeroOrMore(expr) + rbr) 
    expr << Group (key + pair) 

    return expr 

expr = syntax() 
result = expr.parseString(data).asList() 
pprint.pprint(result) 

生產:

[['Continent', 
    ['Name', 'Europe'], 
    ['Country', 
    ['Name', 'UK'], 
    ['Dog', ['Name', 'Fiffi'], ['Colour', 'Gray']], 
    ['Dog', ['Name', 'Smut'], ['Colour', 'Black']]]]] 
+1

使用'split()'分解令牌可用於此示例數據,但並非所有輸入都可以格式化得很好。例如,如果尾部大括號輸入爲「}}}」,則split()將失敗,或者任何條目在引用字符串中包含大括號。 – PaulMcG

+0

@PaulMcGuire是正確的。最初的問題沒有完全指定輸入數據的格式。關於花括號周圍的空格 - 正則表達式可以解決這個問題。如果語法比較複雜,比如正如你所說的那樣,包含引用的sting - 「pyparsing」仍然是更好的選擇。 –

+0

不錯的工作,這是一個比只使用nestedExpr更嚴格的解析器,你保留了鍵+值的關係。歡迎來到pyparsing,Sylvain! – PaulMcG

5

嵌套表達式非常普遍,如果不使用解析庫,通常需要遞歸解析器定義或遞歸代碼。這段代碼對於初學者來說可能令人望而生畏,即使對於專家來說也容易出錯,所以這就是爲什麼我將nestedExpr幫助器添加到pyparsing中的原因。

您遇到的問題是您的輸入字符串不僅僅是一個嵌套的大括號表達式。當我第一次嘗試一個解析器時,我儘量保持測試儘可能簡單 - 例如,我內聯了示例,而不是從文件中讀取它。

test = """\ 
Continent 
{ 
Name Europe 
Country 
{ 
Name UK 
Dog 
{ 
Name Fiffi 
Colour "light Gray" 
} 
Dog 
{ 
Name Smut 
Colour Black 
}}}""" 

from pyparsing import * 

expr = nestedExpr('{','}') 

print expr.parseString(test).asList() 

而且我得到你做同樣的分析錯誤:

Traceback (most recent call last): 
    File "nb.py", line 25, in <module> 
    print expr.parseString(test).asList() 
    File "c:\python26\lib\site-packages\pyparsing-1.5.7-py2.6.egg\pyparsing.py", line 1006, in parseString 
    raise exc 
pyparsing.ParseException: Expected "{" (at char 1), (line:1, col:1) 

所以看的錯誤信息(甚至在自己的代碼進行調試),pyparsing是在開頭字絆腳石「大陸「,因爲這個單詞不是大括號中嵌套表達式的開始,pyparsing(正如我們在異常消息中看到的那樣)正在尋找開頭」{「。

的解決方法是稍微修改您的解析器來處理的介紹「大陸」的標籤,通過改變expr來:

expr = Word(alphas) + nestedExpr('{','}') 

現在,打印出結果列表(使用pprint作爲OP完成,不錯的作品)看起來像:

['Continent', 
['Name', 
    'Europe', 
    'Country', 
    ['Name', 
    'UK', 
    'Dog', 
    ['Name', 'Fiffi', 'Colour', '"light Gray"'], 
    'Dog', 
    ['Name', 'Smut', 'Colour', 'Black']]]] 

這應該匹配您的大括號嵌套。

+0

由於這個問題,我發現了pyparsing - 沒有太多的文檔在線,但確實非常有趣的模塊。有沒有辦法生成一個字典,而不是嵌套列表? –