【问题标题】:Regular Expression Parsing Key Value Pairs in Namelist Input File正则表达式解析名称列表输入文件中的键值对
【发布时间】:2012-12-19 09:34:06
【问题描述】:

我有一个 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,} 或任何在下一个逗号之前与[^=,]

这些有点乱的正则表达式是解析这样一个文件的最佳方法吗?

【问题讨论】:

  • 你为什么要在这里坚持使用正则表达式?另外,为什么顺序很重要?我的印象是 fortran 名称列表没有排序..
  • 我正在构建一个用于编辑输入文件的应用程序。一些名单有数百个条目,这些条目通常被分组到逻辑类别中。此外,相关或相互依赖的输入通常一起列出,以便可以轻松地一起编辑。我希望能够读取输入文件,提供用于编辑各种参数的 GUI,然后以相同的顺序将其写回。实际上,我已经有了 GUI 和导出功能(使用硬编码数据集),我现在正在编写一个导入函数。

标签: python regex parsing key-value


【解决方案1】:

我建议开发更复杂的解析器。

我偶然发现了实现非常相似的解析器功能的 google 代码托管项目: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']}}

【讨论】:

  • 非常感谢您在此回复中所做的努力!我最终找到了您提到的 Fortran Namelist Parser,并从中窃取了一些正则表达式,以帮助确定每个键值对的值的数据类型。最后,我坚持使用我相当简单的正则表达式解析方法,因为我理解它。不过,您的回答绝对是针对我的问题的强大、量身定制的解决方案!为你的辛勤工作点赞!也许我最终会用它来改进我的代码,一旦我能理解它所做的一切!
  • 谢谢,这非常有用!在我看来,这比之前的代码版本更上一层楼。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-28
  • 2012-07-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多