Skip to main content
 首页 » 编程设计

python之无法使用 pyparsing 正确解析此文件

2025年05月04日6mayingbao

我正在尝试使用神奇的 python 库 pyparsing 解析文件,但我遇到了很多问题......

我要解析的文件是这样的:

sectionOne: 
  list: 
  - XXitem 
  - XXanotherItem 
  key1: value1 
  product: milk 
  release: now 
  subSection: 
    skey : sval 
    slist: 
    - XXitem 
  mods: 
  - XXone 
  - XXtwo 
  version: last 
sectionTwo: 
  base: base-0.1 
  config: config-7.0-7 

如您所见,这是一个缩进的配置文件,这或多或少是我尝试定义语法的方式

  • 文件可以有一个或多个部分
  • 每个部分由部分名称和部分内容组成。
  • 每个部分都有一个缩进的内容
  • 每个部分内容可以有一对或多对键/值或一个子部分。
  • 每个值可以只是一个单词或一个项目列表。
  • 项目列表是一组一个或多个项目。
  • 每一项都是连​​字符 + 以“XX”开头的名称

我曾尝试使用 pyparsing 创建此语法,但没有成功。

import pprint 
import pyparsing 
NEWLINE = pyparsing.LineEnd().suppress() 
VALID_CHARACTERS = pyparsing.srange("[a-zA-Z0-9_\-\.]") 
COLON = pyparsing.Suppress(pyparsing.Literal(":")) 
HYPHEN = pyparsing.Suppress(pyparsing.Literal("-")) 
XX = pyparsing.Literal("XX") 
 
list_item = HYPHEN + pyparsing.Combine(XX + pyparsing.Word(VALID_CHARACTERS)) 
list_of_items = pyparsing.Group(pyparsing.OneOrMore(list_item)) 
 
key = pyparsing.Word(VALID_CHARACTERS) + COLON 
pair_value = pyparsing.Word(VALID_CHARACTERS) + NEWLINE 
value = (pair_value | list_of_items) 
 
pair = pyparsing.Group(key + value) 
 
indentStack = [1] 
 
section = pyparsing.Forward() 
section_name = pyparsing.Word(VALID_CHARACTERS) + COLON 
section_value = pyparsing.OneOrMore(pair | section) 
section_content = pyparsing.indentedBlock(section_value, indentStack, True) 
 
section << pyparsing.Group(section_name + section_content) 
 
parser = pyparsing.OneOrMore(section) 
 
def main(): 
    try: 
        with open('simple.info', 'r') as content_file: 
            content = content_file.read() 
 
            print "content:\n", content 
            print "\n" 
            result = parser.parseString(content) 
            print "result1:\n", result 
            print "len", len(result) 
 
            pprint.pprint(result.asList()) 
    except pyparsing.ParseException, err: 
        print err.line 
        print " " * (err.column - 1) + "^" 
        print err 
    except pyparsing.ParseFatalException, err: 
        print err.line 
        print " " * (err.column - 1) + "^" 
        print err 
 
 
if __name__ == '__main__': 
    main() 

这是结果:

result1: 
  [['sectionOne', [[['list', ['XXitem', 'XXanotherItem']], ['key1', 'value1'], ['product', 'milk'], ['release', 'now'], ['subSection', [[['skey', 'sval'], ['slist', ['XXitem']], ['mods', ['XXone', 'XXtwo']], ['version', 'last']]]]]]], ['sectionTwo', [[['base', 'base-0.1'], ['config', 'config-7.0-7']]]]] 
  len 2 
  [ 
     ['sectionOne', 
     [[ 
        ['list', ['XXitem', 'XXanotherItem']], 
        ['key1', 'value1'], 
        ['product', 'milk'], 
        ['release', 'now'], 
        ['subSection', 
           [[ 
              ['skey', 'sval'], 
              ['slist', ['XXitem']], 
              ['mods', ['XXone', 'XXtwo']], 
              ['version', 'last'] 
           ]] 
        ] 
     ]] 
     ], 
     ['sectionTwo',  
     [[ 
        ['base', 'base-0.1'],  
        ['config', 'config-7.0-7'] 
     ]] 
     ] 
  ] 

如您所见,我有两个主要问题:

1.- 每个部分的内容在一个列表中嵌套两次

2.- 当键“version”属于“sectionOne”时,在“subSection”中解析

我的真正的目标是能够获得一个包含键和值的 python 嵌套字典结构,以便轻松提取每个字段的信息,但是 pyparsing.Dict 对我来说有些晦涩难懂。

有人能帮帮我吗?

提前致谢

(很抱歉发了这么长的帖子)

请您参考如下方法:

你真的很接近 - 恭喜,缩进解析器不是用 pyparsing 最容易编写的。

查看注释的更改。标有“A”的是为解决您提出的两个问题所做的更改。那些标有“B”的添加了 Dict 结构,以便您可以使用配置中的名称将解析后的数据作为嵌套结构访问。

最大的罪魁祸首是 indentedBlock 为您做了一些额外的分组,这妨碍了 Dict 的名称-值关联。使用 ungroup 将其剥离,让 Dict 看到底层对。

祝 pyparsing 好运!

import pprint 
import pyparsing 
NEWLINE = pyparsing.LineEnd().suppress() 
VALID_CHARACTERS = pyparsing.srange("[a-zA-Z0-9_\-\.]") 
COLON = pyparsing.Suppress(pyparsing.Literal(":")) 
HYPHEN = pyparsing.Suppress(pyparsing.Literal("-")) 
XX = pyparsing.Literal("XX") 
 
list_item = HYPHEN + pyparsing.Combine(XX + pyparsing.Word(VALID_CHARACTERS)) 
list_of_items = pyparsing.Group(pyparsing.OneOrMore(list_item)) 
 
key = pyparsing.Word(VALID_CHARACTERS) + COLON 
pair_value = pyparsing.Word(VALID_CHARACTERS) + NEWLINE 
value = (pair_value | list_of_items) 
 
#~ A: pair = pyparsing.Group(key + value) 
pair = (key + value) 
 
indentStack = [1] 
 
section = pyparsing.Forward() 
section_name = pyparsing.Word(VALID_CHARACTERS) + COLON 
#~ A: section_value = pyparsing.OneOrMore(pair | section) 
section_value = (pair | section) 
 
#~ B: section_content = pyparsing.indentedBlock(section_value, indentStack, True) 
section_content = pyparsing.Dict(pyparsing.ungroup(pyparsing.indentedBlock(section_value, indentStack, True))) 
 
#~ A: section << Group(section_name + section_content) 
section << (section_name + section_content) 
 
#~ B: parser = pyparsing.OneOrMore(section) 
parser = pyparsing.Dict(pyparsing.OneOrMore(pyparsing.Group(section))) 

现在你可以这样写:

print (result.dump()) 

显示字典层次结构:

[['sectionOne', ['list', ['XXitem', 'XXanotherItem']], ... etc. ... 
- sectionOne: [['list', ['XXitem', 'XXanotherItem']], ... etc. ... 
  - key1: value1 
  - list: ['XXitem', 'XXanotherItem'] 
  - mods: ['XXone', 'XXtwo'] 
  - product: milk 
  - release: now 
  - subSection: [['skey', 'sval'], ['slist', ['XXitem']]] 
    - skey: sval 
    - slist: ['XXitem'] 
  - version: last 
- sectionTwo: [['base', 'base-0.1'], ['config', 'config-7.0-7']] 
  - base: base-0.1 
  - config: config-7.0-7 

允许您编写如下语句:

print (result.sectionTwo.base)