我需要解析一个文件,其中的信息用大括号分隔,例如:
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
我需要做些什么来完成这项工作? 另一种解析器比pypars更好吗?
答案 0 :(得分: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在主要词“Continent”上磕磕绊绊,因为这个词不是括号中的嵌套表达式的开头,pyparsing(正如我们所见)异常消息)正在寻找一个开头'{'。
解决方案是稍微修改您的解析器以处理介绍性的“Continent”标签,方法是将expr更改为:
expr = Word(alphas) + nestedExpr('{','}')
现在,将结果打印出来作为列表(使用pprint,如OP中所做的那样,做得很好)看起来像:
['Continent',
['Name',
'Europe',
'Country',
['Name',
'UK',
'Dog',
['Name', 'Fiffi', 'Colour', '"light Gray"'],
'Dog',
['Name', 'Smut', 'Colour', 'Black']]]]
应该与你的大括号嵌套相匹配。
答案 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']]]]]