我正在开发一个项目,我想用Python解析文本文件。该文件由一些数据条目组成,这些数据条目的格式可以变化。有新行时会找到新条目。这就是我想要实现的目标:
以下是该文件的示例:
Header Info
More Header Info
Line1
Line2
Line3
Line4
Line5
Line6
Line7
Line8
Line9
Line10
Line11
Line12
Line13
MoreInfo MoreInfo MoreInfo MoreInfo MoreInfo
MoreInfo2 MoreInfo2 MoreInfo2 MoreInfo2 MoreInfo2 MoreInfo2
MoreInfo3 MoreInfo3 MoreInfo3 MoreInfo3 MoreInfo3
MoreInfo4 MoreInfo4
FieldName1 0001 0001
FieldName1 0002 0002
FieldName1 0003 0003
FieldName1 0004 0004
FieldName1 0005 0005
FieldName2 0001 0001
FieldName3 0001 0001
FieldName4 0001 0001
FieldName5 0001 0001
FieldName6 0001 0001
MoreInfo MoreInfo MoreInfo MoreInfo MoreInfo
MoreInfo2 MoreInfo2 MoreInfo2 MoreInfo2 MoreInfo2 MoreInfo2
MoreInfo3 MoreInfo3 MoreInfo3 MoreInfo3 MoreInfo3
MoreInfo4 MoreInfo4
FieldName1 0001 0001
FieldName1 0002 0002
FieldName1 0003 0003
FieldName1 0004 0004
FieldName1 0005 0005
FieldName2 0001 0001
FieldName3 0001 0001
FieldName4 0001 0001
FieldName5 0001 0001
FieldName6 0001 0001
以下是我参与过的一些代码。它能够读取第一个块并将其附加到列表中:
with open(loc, 'r') as f:
for i in range(16):
f.readline()
data = []
line = f.readline()
if line == "\n":
dataLine = f.readline()
while dataLine != "\n":
data.append(dataLine)
dataLine = f.readline()
#pass data list to function
function_call(data)
# reset data list here?
data = []
如何使其适用于完整文件?我的假设是使用“with open”,它充当“而不是文件结束”。跳过前16行后,我尝试添加“while True”。 我对Python的解析功能知之甚少。
感谢您提前获取任何帮助。
答案 0 :(得分:3)
在初始跳过后添加while True
肯定会有用。当然,你必须正确地了解所有细节。
您可以尝试扩展您已有的方法,在外部循环中使用嵌套的while
循环。但将它视为单循环可能更容易。对于每一行,您可能只需做三件事:
break
退出循环,确保处理旧的data
(最后一个块)在文件中)如果有第一个。data
,确保先处理旧的data
。data
。所以:
with open(loc, 'r') as f:
for i in range(16):
f.readline()
data = []
while True:
line = f.readline()
if not line:
if data:
function_call(data)
break
if line == "\n":
if data:
function_call(data)
data = []
else:
data.append(line)
有几种方法可以进一步简化:
for line in f:
而不是重复执行while
的{{1}}循环并对其进行检查。groupby
将行的迭代器转换为以空行分隔的行组的迭代器。答案 1 :(得分:0)
如果您仍在努力解决这个问题,那么可以使用itertools.groupby()
和关键功能search()
来读取您的示例数据:
from itertools import groupby, repeat
def search(d):
"""Key function used to group our dataset"""
return d[0] == "\n"
def read_data(filename):
"""Read data from filename and return a nicer data structure"""
data = []
with open(filename, "r") as f:
# Skip first 16 lines
for _ in repeat(None, 16):
f.readline()
# iterate through each data block
for newblock, records in groupby(f, search):
if newblock:
# we've found a new block
# create a new row of data
data.append([])
else:
# we've found data for the current block
# add each row to the last row
for row in records:
row = row.strip().split()
data[-1].append(row)
return data
这将导致数据结构是嵌套的块列表。每个子列表由数据文件中的\n
分组分开。
答案 2 :(得分:0)
文件中的块模式是由以空行或文件末尾结尾的行组成。这个逻辑可以封装在一个生成器函数中,该函数迭代地从您的文件中生成行块,这将简化脚本的其余部分。
在下文中,getlines()
是生成器函数。另请注意,文件的第一行 17 会被跳过,以便到达第一个块的开头。
from pprint import pformat
loc = 'parsing_test_file.txt'
def function(lines):
print('function called with:\n{}'.format(pformat(lines)))
def getlines(f):
lines = []
while True:
try:
line = next(f)
if line != '\n': # not end of the block?
lines.append(line)
else:
yield lines
lines = []
except StopIteration: # end of file
if lines:
yield lines
break
with open(loc, 'r') as f:
for i in range(17):
next(f)
for lines in getlines(f):
function(lines)
print('done')
使用测试文件输出:
function called with:
['MoreInfo MoreInfo MoreInfo MoreInfo MoreInfo\n',
'MoreInfo2 MoreInfo2 MoreInfo2 MoreInfo2 MoreInfo2 MoreInfo2\n',
'MoreInfo3 MoreInfo3 MoreInfo3 MoreInfo3 MoreInfo3\n',
'MoreInfo4 MoreInfo4\n',
'FieldName1 0001 0001\n',
'FieldName1 0002 0002\n',
'FieldName1 0003 0003\n',
'FieldName1 0004 0004\n',
'FieldName1 0005 0005\n',
'FieldName2 0001 0001\n',
'FieldName3 0001 0001\n',
'FieldName4 0001 0001\n',
'FieldName5 0001 0001\n',
'FieldName6 0001 0001\n']
function called with:
['MoreInfo MoreInfo MoreInfo MoreInfo MoreInfo\n',
'MoreInfo2 MoreInfo2 MoreInfo2 MoreInfo2 MoreInfo2 MoreInfo2\n',
'MoreInfo3 MoreInfo3 MoreInfo3 MoreInfo3 MoreInfo3\n',
'MoreInfo4 MoreInfo4\n',
'FieldName1 0001 0001\n',
'FieldName1 0002 0002\n',
'FieldName1 0003 0003\n',
'FieldName1 0004 0004\n',
'FieldName1 0005 0005\n',
'FieldName2 0001 0001\n',
'FieldName3 0001 0001\n',
'FieldName4 0001 0001\n',
'FieldName5 0001 0001\n',
'FieldName6 0001 0001\n']
done