Python+;解析自定义配置文件
我有一个相当大的定制配置文件,我需要每周提取一次数据。这是一个“内部”配置文件,不符合任何已知标准,如INI或类似标准 我快速而肮脏的方法是使用re搜索我想要的节标题,然后在此标题下提取我想要的一行或两行信息。事实证明,这是一个相当大的挑战,我认为必须有一种更简单/更可靠的方法来做到这一点,但我一直认为我需要实现一个完整的解析器来解析这个文件,然后只提取我需要的5行数据 “部分”如下所示:Python+;解析自定义配置文件,python,file,parsing,config,Python,File,Parsing,Config,我有一个相当大的定制配置文件,我需要每周提取一次数据。这是一个“内部”配置文件,不符合任何已知标准,如INI或类似标准 我快速而肮脏的方法是使用re搜索我想要的节标题,然后在此标题下提取我想要的一行或两行信息。事实证明,这是一个相当大的挑战,我认为必须有一种更简单/更可靠的方法来做到这一点,但我一直认为我需要实现一个完整的解析器来解析这个文件,然后只提取我需要的5行数据 “部分”如下所示: Registry com.name.version = Registry "unique-name I s
Registry com.name.version =
Registry "unique-name I search for using re" =
String name = "modulename";
String timestamp = "not specified";
String java = "not specified";
String user = "not specified";
String host = "not specified";
String system = "not specified";
String version = "This I want";
String "version-major" = "not specified";
String "version-minor" = "not specified";
String scm = "not specified";
String scmrevision = "not specified";
String mode = "release";
String teamCityBuildNumber = "not specified";
;
Regexp没有状态,因此不能使用它们来解析复杂的输入。但您可以将文件加载到字符串中,使用regexp查找子字符串,然后在该位置剪切字符串
在您的情况下,使用re搜索
r'unique-name“\s*=\s*'
,然后在匹配后剪切。然后搜索r'\n\s*\s*\n'
并在比赛前剪掉。这就为您留下了可以使用另一个regexp切碎的值。如果您只查找特殊内容,使用regexp就可以了;如果您需要阅读所有内容,您应该为自己构建一个解析器
>> s = ''' ... ''' # as above
>> t = re.search( 'Registry "unique-name" =(.*?)\n;', s, re.S ).group( 1 )
>> u = re.findall( '^\s*(\w+) "?(.*?)"? = "(.*?)";\s*$', t, re.M )
>> for x in u:
print( x )
('String', 'name', 'modulename')
('String', 'timestamp', 'not specified')
('String', 'java', 'not specified')
('String', 'user', 'not specified')
('String', 'host', 'not specified')
('String', 'system', 'not specified')
('String', 'version', 'This I want')
('String', 'version-major', 'not specified')
('String', 'version-minor', 'not specified')
('String', 'scm', 'not specified')
('String', 'scmrevision', 'not specified')
('String', 'mode', 'release')
编辑:尽管上述版本适用于多个注册表部分,但这里有一个更严格的版本:
t = re.search( 'Registry "unique-name"\s*=\s*((?:\s*\w+ "?[^"=]+"?\s*=\s*"[^"]*?";\s*)+)\s*;', s ).group( 1 )
u = re.findall( '^\s*(\w+) "?([^"=]+)"?\s*=\s*"([^"]*?)";\s*$', t, re.M )
我认为您应该创建一个简单的解析器,它使用键字典创建节字典。比如:
#!/usr/bin/python
import re
re_section = re.compile('Registry (.*)=', re.IGNORECASE)
re_value = re.compile('\s+String\s+(\S+)\s*=\s*(.*);')
txt = '''
Registry com.name.version =
Registry "unique-name I search for using re" =
String name = "modulename";
String timestamp = "not specified";
String java = "not specified";
String user = "not specified";
String host = "not specified";
String system = "not specified";
String version = "This I want";
String "version-major" = "not specified";
String "version-minor" = "not specified";
String scm = "not specified";
String scmrevision = "not specified";
String mode = "release";
String teamCityBuildNumber = "not specified";
'''
my_config = {}
section = ''
lines = txt.split('\n')
for l in lines:
rx = re_section.search(l)
if rx:
section = rx.group(1)
section = section.strip('" ')
continue
rx = re_value.search(l)
if rx:
(k, v) = (rx.group(1).strip('" '), rx.group(2).strip('" '))
try:
my_config[section][k] = v
except KeyError:
my_config[section] = {k: v}
如果你:
print my_config["unique-name I search for using re"]['version']
它将输出:
This I want
使用pyparsing的简单解析器可以为您提供类似于反序列化器的功能,让您可以通过键名(如dict)或属性访问字段。以下是解析器:
from pyparsing import (Suppress,quotedString,removeQuotes,Word,alphas,
alphanums, printables,delimitedList,Group,Dict,ZeroOrMore,OneOrMore)
# define punctuation and constants - suppress from parsed output
EQ,SEMI = map(Suppress,"=;")
REGISTRY = Suppress("Registry")
STRING = Suppress("String")
# define some basic building blocks
quotedString.setParseAction(removeQuotes)
ident = quotedString | Word(printables)
value = quotedString
java_path = delimitedList(Word(alphas,alphanums+"_"), '.', combine=True)
# define the config file sections
string_defn = Group(STRING + ident + EQ + value + SEMI)
registry_section = Group(REGISTRY + ident + EQ + Dict(ZeroOrMore(string_defn)))
# special definition for leading java module
java_module = REGISTRY + java_path("path") + EQ
# define the overall config file format
config = java_module("java") + Dict(OneOrMore(registry_section))
下面是一个使用数据的测试(从数据文件读入config_源):
印刷品:
['com.name.version', ['unique-name I search for using re', ...
- java: ['com.name.version']
- path: com.name.version
- path: com.name.version
- unique-name I search for using re: [['name', 'modulename'], ...
- host: not specified
- java: not specified
- mode: release
- name: modulename
- scm: not specified
- scmrevision: not specified
- system: not specified
- teamCityBuildNumber: not specified
- timestamp: not specified
- user: not specified
- version: This I want
- version-major: not specified
- version-minor: not specified
This I want
release
not specified
那么你想提取什么呢?关于这种格式有两个问题:分号是否总是在这样一个“注册表”块末尾的自己的行上?为什么在最上面的一行没有一个呢?第一行总是java模块路径(或者不管你怎么称呼它,我不太懂java),这一部分总是以分号结尾。有时候这甚至可以是一行。这听起来很合理,我会仔细研究一下这个方法,谢谢Aaron。啊!这正好说明我还有很多关于regexp的知识要学!谢谢你!,这很有帮助,也很有教育意义:-)poke,我在理解整个regexp时遇到了一些问题。您的示例似乎找到了正确的部分,但当它到达“;”时,它不会停止在该部分的and处,因此它会继续读取此后的所有部分。有没有可能这样做,我只提取这一个完整的部分?这对我来说是可行的(我在测试中添加了另一个注册表部分),但为了安全起见,我更新了我的帖子,加入了一个更严格的表达式,另外确保只有那些设置行在后面。
['com.name.version', ['unique-name I search for using re', ...
- java: ['com.name.version']
- path: com.name.version
- path: com.name.version
- unique-name I search for using re: [['name', 'modulename'], ...
- host: not specified
- java: not specified
- mode: release
- name: modulename
- scm: not specified
- scmrevision: not specified
- system: not specified
- teamCityBuildNumber: not specified
- timestamp: not specified
- user: not specified
- version: This I want
- version-major: not specified
- version-minor: not specified
This I want
release
not specified