我有一个Fortran“名称列表”格式的输入文件,我想用python正则表达式来解析它。最简单的演示方法是用一个虚构的例子:
$VEHICLES
CARS= 1,
TRUCKS = 0,
PLAINS= 0, TRAINS = 0,
LIB='AUTO.DAT',
C This is a comment
C Data variable spans multiple lines
DATA=1.2,2.34,3.12,
4.56E-2,6.78,
$END
$PLOTTING
PLOT=T,
PLOT(2)=12,
$END因此,键可以包含常规的变量名字符以及括号和数字。这些值可以是字符串、布尔值(T、F、.T、.F、TRUE、FALSE、.TRUE、.FALSE。都是可能的)、整数、浮点数或逗号分隔的数字列表。键与它们的值之间用等号连接。键-值对用逗号分隔,但可以共享一行。对于很长的数字列表,值可以跨越多行。注释是以C开头的任何行。'=‘和',’前后的间距通常不一致。
我提出了一个有效的正则表达式,用于解析键和值,并将它们放入有序字典中(需要保持输入的顺序)。
到目前为止,我的代码如下。我已经包含了从读取文件到保存到字典的所有内容,以确保完整性。
import re
from collections import OrderedDict
f=open('file.dat','r')
file_str=f.read()
#Compile regex pattern for requested namelist
name='Vehicles'
p_namelist = re.compile(r"\$"+name.upper()+"(.*?)\$END",flags=re.DOTALL|re.MULTILINE)
#Execute regex on file string and get a list of captured tokens
m_namelist = p_namelist.findall(file_str)
#Check for a valid result
if m_namelist:
#The text of the desired namelist is the first captured token
namelist=m_namelist[0]
#Split into lines
lines=namelist.splitlines()
#List comprehension which returns the list of lines that do not start with "C"
#Effectively remove comment lines
lines = [item for item in lines if not item.startswith("C")]
#Re-combine now that comment lines are removed
namelist='\n'.join(lines)
#Create key-value parsing regex
p_item = re.compile(r"([^\s,\=]+?)\s*=\s*([^=]+)(?=[\s,][^\s,\=]+\s*\=|$)",flags=re.DOTALL|re.MULTILINE)
#Execute regex
items = p_item.findall(namelist)
#Initialize namelist ordered dictionary
n = OrderedDict()
#Remove undesired characters from value
for item in items:
n[item[0]] = item[1].strip(',\r\n ')我的问题是我是否正确地进行了这项工作。我意识到有一个ConfigParser库,我还没有尝试过。这里我的重点是正则表达式:
([^\s,\=]+?)\s*=\s*([^=]+)(?=[\s,][^\s,\=]+\s*\=|$)但我继续并包含了其他代码,以确保完整性,并演示我正在用它做什么。对于我的正则表达式,因为值可以包含逗号,并且键-值对也用逗号分隔,所以没有简单的方法来隔离这些对。我选择使用前瞻来查找下一个键和"=“。这允许"=“和下一个键之间的所有内容都是值。最后,由于这对最后一对不起作用,我将"|$“添加到了前视中,这意味着如果没有找到另一个"VALUE=”,则查找字符串的末尾。我认为先将值与^=+进行匹配,然后再进行先行检查,总比尝试匹配所有可能的值类型要好。
在写这个问题的时候,我想出了一个替代的正则表达式,它利用了数字是列表中唯一的值的事实:
([^\s,\=]+?)\s*=\s*((?:\s*\d[\d\.\E\+\-]*\s*,){2,}|[^=,]+)它可以用(?:\s*\d[\d\.\E\+\-]*\s*,){2,}匹配包含2个或更多数字的列表,也可以用[^=,]匹配下一个逗号之前的任何数字。
这些有点混乱的正则表达式是解析像这样的文件的最佳方法吗?
发布于 2013-01-11 06:41:22
我建议开发更复杂一点的解析器。
我偶然发现了一个关于谷歌代码托管的项目,它实现了非常相似的解析器功能:Fortran Namelist parser for Python prog/scripts,但它是为稍微不同的格式构建的。我尝试了一下,并更新了它,以支持您示例中的格式结构:
请看我在gist上的版本:Updated Fortran Namelist parser for python https://gist.github.com/4506282
我希望这个解析器能对你的项目有所帮助。
以下是脚本在解析FORTRAN代码后产生的示例输出示例:
{'PLOTTING':
{'par':
[OrderedDict([('PLOT', ['T']), ('PLOT(2) =', ['12'])])],
'raw': ['PLOT=T', 'PLOT(2)=12']},
'VEHICLES':
{'par':
[OrderedDict([('TRUCKS', ['0']), ('PLAINS', ['0']), ('TRAINS', ['0']), ('LIB', ['AUTO.DAT']), ('DATA', ['1.2', '2.34', '3.12', '4.56E-2', '6.78'])])],
'raw':
['TRUCKS = 0',
'PLAINS= 0, TRAINS = 0',
"LIB='AUTO.DAT'",
'DATA=1.2,2.34,3.12',
'4.56E-2,6.78']}}https://stackoverflow.com/questions/14165733
复制相似问题