如何在Python中从不规则格式的数据文件中提取数据
我需要从一个文件中提取特定的数据,但这个文件是为人类阅读而格式化的,所以它的格式比较不规则。首先,在数据开始之前,有一大堆文本:
DL_POLY Version 2.20 Running on 10 nodes
*************** DLPOLY: LiNbO3 >***************
模拟控制参数
模拟温度 1.4500E+03
模拟压力 (katm) 0.0000E+00
选择的时间步数 8000
平衡期 500
数据打印间隔 80
统计文件间隔 80
模拟时间步长 5.0000E-04
Nose-Hoover (Melchionna) 各向同性 N-P-T 温控器放松时间 1.0000E-01 压控器放松时间 5.0000E-01
轨迹文件选项开启
轨迹文件起始 1 轨迹文件间隔 80 轨迹文件信息键 2 ...
然后过了一段时间,实际数据出现了,但它的格式有点奇怪:
步骤 总能量 总温度 配置能量 劣势能量 互作用能量 键能量 > 角能量 二面角能量 四面角能量 时间(ps) 动量能量 旋转温度 配置虚能量 劣势虚能量 互作用虚能量 键虚能量 >虚角能量 虚约束 虚四面角 CPU (秒) 体积 剪切温度 剪切能量 虚剪切 alpha beta >gamma 虚自由能 压力
1 -1.1289E+05 1.4750E+03 -1.1386E+05 1.7276E+04 -1.3114E+05 0.0000E+00 >0.0000E+00 0.0000E+00 0.0000E+00 0.0 -1.1545E+05 0.0000E+00 9.6539E+03 -1.2118E+05 1.3083E+05 0.0000E+00 >0.0000E+00 0.0000E+00 0.0000E+00 0.8 5.3733E+04 1.2367E+02 0.0000E+00 0.0000E+00 5.6396E+01 5.6396E+01 >5.6396E+01 0.0000E+00 -7.5549E+01
滚动 -1.1289E+05 1.4750E+03 -1.1386E+05 1.7276E+04 -1.3114E+05 0.0000E+00 >0.0000E+00 0.0000E+00 0.0000E+00 平均值 -1.1545E+05 0.0000E+00 9.6539E+03 -1.2118E+05 1.3083E+05 0.0000E+00 >0.0000E+00 0.0000E+00 0.0000E+00 5.3733E+04 1.2367E+02 0.0000E+00 0.0000E+00 5.6396E+01 5.6396E+01 >5.6396E+01 0.0000E+00 -7.5549E+01
80 -1.1290E+05 1.5021E+03 -1.1392E+05 2.1894E+04 -1.3726E+05 0.0000E+00 >0.0000E+00 0.0000E+00 0.0000E+00 0.0 -1.1256E+05 0.0000E+00 8.6671E+02 -1.3974E+05 1.3707E+05 0.0000E+00 >0.0000E+00 0.0000E+00 0.0000E+00 10.6 5.3149E+04 1.1377E+03 1.4419E+03 3.5382E+03 5.6396E+01 5.6396E+01 >5.6396E+01 0.0000E+00 1.1119E+01
滚动 -1.1290E+05 1.6145E+03 -1.1398E+05 2.0750E+04 -1.3588E+05 0.0000E+00 >0.0000E+00 0.0000E+00 0.0000E+00 平均值 -1.1333E+05 0.0000E+00 3.3694E+03 -1.3512E+05 1.3565E+05 0.0000E+00 >0.0000E+00 0.0000E+00 0.0000E+00 5.3481E+04 1.0997E+03 1.1430E+03 2.8391E+03 5.6396E+01 5.6396E+01 >5.6396E+01 0.0000E+00 -1.2096E+01
160 -1.1287E+05 1.2629E+03 -1.1376E+05 2.1450E+04 -1.3633E+05 0.0000E+00 >0.0000E+00 0.0000E+00 0.0000E+00 0.1 -1.1249E+05 0.0000E+00 3.8761E+02 -1.3824E+05 1.3612E+05 0.0000E+00 >0.0000E+00 0.0000E+00 0.0000E+00 20.5 5.3375E+04 4.9015E+02 1.1243E+03 2.5052E+03 5.6396E+01 5.6396E+01 >5.6396E+01 0.0000E+00 1.2676E+01
滚动 -1.1288E+05 1.4677E+03 -1.1389E+05 2.1589E+04 -1.3663E+05 0.0000E+00 0.0000E+00 0.0000E+00 0.0000E+00 平均值 -1.1235E+05 0.0000E+00 2.1147E+02 -1.3884E+05 1.3643E+05 0.0000E+00 >0.0000E+00 0.0000E+00 0.0000E+00 5.3152E+04 7.4818E+02 1.1440E+03 2.6211E+03 5.6396E+01 5.6396E+01 >5.6396E+01 0.0000E+00 1.7174E+01
在第9个数据间隔时,有一个小异常:
在步骤500时关闭温度缩放
560 -1.1287E+05 1.4709E+03 -1.1390E+05 2.1600E+04 -1.3678E+05 0.0000E+00 >0.0000E+00 0.0000E+00 0.0000E+00 0.3 -1.1292E+05 0.0000E+00 1.9253E+03 -1.3743E+05 1.3656E+05 0.0000E+00 >0.0000E+00 0.0000E+00 0.0000E+00 68.4 5.4300E+04 1.5043E+02 1.2775E+03 2.7947E+03 5.6396E+01 5.6396E+01 >5.6396E+01 0.0000E+00 2.0576E-01
滚动 -1.1286E+05 1.4784E+03 -1.1390E+05 2.1546E+04 -1.3673E+05 0.0000E+00 >0.0000E+00 0.0000E+00 0.0000E+00 平均值 -1.1298E+05 0.0000E+00 2.1361E+03 -1.3717E+05 1.3651E+05 0.0000E+00 >0.0000E+00 0.0000E+00 0.0000E+00 5.4303E+04 2.2261E+02 1.2785E+03 2.8027E+03 5.6396E+01 5.6396E+01 >5.6396E+01 0.0000E+00 -1.7421E+00
可以看到,有一对'----'的行,这可能会干扰数据的正确解析。
假设我只想从这个文件中获取'eng_tot'的数据(加粗的数字),我该如何在Python中做到这一点?这个数字总是在文件中的同一个位置(第二个数量,第二组'----'之后的第一行)。
顺便提一下,包含所有定义的标题部分每8步就会重复一次,除了第一步有9行。我想忽略第一步。现在假设我想从第295行开始,包括这一行。告诉你,我对Python和编程都很陌生,所以你能提供的任何帮助我都非常感激。
这是我尝试过的代码,但Eng_Total仍然是一个空集合:
import re
import inspect
def lineno():
"""Returns the current line number"""
linenum = inspect.currentframe().f_back.f_lineno
infile = open('FilePath/OUTPUT.01').read()
Eng_Total = []
for line in infile:
# if 'eng_tot' in line.split():
if re.match("\s+-+\s+", line):
lineno(line)
line = linenum+1
sanitized_line = line[8:]
eng_total = line.split()[0]
Eng_Total.append(eng_total)
print Eng_Total
2 个回答
你需要明确地定义文件格式,然后就可以轻松解析它。
第一步是找出你需要的数据在哪里定义。然后把之前的内容都丢掉,从那个地方开始读取。
如果eng_tot
的位置可能会变动,你需要弄清楚它在有用数据块中的位置。所以,先读一行,entries = line.split(); location = entries.index('eng_tot')
,然后从输出数据的对应行中读取那个位置的内容。
关键是你需要把问题分解成你能做到的步骤。当面对新事物时,很容易感到不知所措。如果你能先开始做一些事情,你会发现其实可以比较顺利地找到解决方案。
我可能会这样做:
- 遍历输出中的每一行
- 寻找包含
eng_tot
的行:if 'eng_tot' in line.split(): process_blocks
- 一直读取行,直到找到一行全是破折号的行(两边可以有空格)
if re.match("\s+-+\s+", line): proccess_metrics_block
- 处理指标的第一行:
- 把这一行的第一列去掉(因为它可能不在,所以会让解析变得复杂)
sanitized_line = line[8:]
eng_total = line.split()[0]
,现在第一列就是 eng_total
- 把这一行的第一列去掉(因为它可能不在,所以会让解析变得复杂)
- 跳过行,直到再遇到一行破折号,然后重新开始
在看到你的修改后:
- 你需要在文件顶部导入
re
(正则表达式)模块:import re
process_blocks
和process_metrics_block
是伪代码。除非你自己定义它们,否则它们是不存在的。:) 你不一定需要这些函数,可以用基本的循环(while
)和条件(if
)语句来替代。- 你得确保自己明白在做什么,而不是光复制 Stack Overflow 上的内容! :)
看起来你想做的事情大概是这样的。虽然似乎能工作,但我相信只要稍微努力,你可以想出更好的方法:
import re
def find_header(lines):
for (i, line) in enumerate(lines):
if 'eng_tot' in line.split():
return i
return None
def find_next_separator(lines, start):
for (i, line) in enumerate(lines[start+1:]):
if re.match("\s*-+\s*", line):
return i + start + 1
return None
if __name__ == '__main__':
totals = []
lines = open('so.txt').readlines()
header = find_header(lines)
start = find_next_separator(lines, header+1)
while True:
end = find_next_separator(lines, start+1)
if end is None: break
# Pull out block, after line of dashes.
metrics_block = lines[start+1:end]
# Pull out 2nd column from 1st line of metrics.
eng_total = metrics_block[0].split()[1]
totals.append(eng_total)
start = end
print totals
你可以使用生成器,让代码更符合 Python 的风格:
def metric_block_iter(lines):
start = find_next_separator(lines, find_header(lines)+1)
while True:
end = find_next_separator(lines, start+1)
if end is None: break
yield (start, end)
start = end
if __name__ == '__main__':
totals = []
lines = open('so.txt').readlines()
for (start, end) in metric_block_iter(lines):
# Pull out block, after line of dashes.
metrics_block = lines[start+1:end]
# Pull out 2nd column from 1st line of metrics.
eng_total = metrics_block[0].split()[1]
totals.append(eng_total)
print totals