解析换行分隔文件
Parsing newline delimited file
我正在做一个项目,我想在其中使用 Python 解析文本文件。该文件由一些格式不同的块的数据条目组成。有新行时会找到新条目。这就是我想要完成的:
- 跳过前几行(前 16 行)
- 第16行后,有一个换行符开始新的数据输入
- 阅读以下行,直到遇到新的换行符。每行都附加到名为数据的列表中。
- 该列表将被传递给处理进一步处理的函数。
- 重复步骤 3 和 4,直到文件中没有更多数据
以下是文件示例:
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",它充当 "while not end of file"。我尝试在跳过前 16 行后添加 "while True"。
我对Python的解析能力知之甚少。
在此先感谢您的帮助。
在初始跳过后添加一个 while True
绝对有效。当然,你必须把所有的细节都弄好。
您可以尝试扩展您已有的方法,在外循环中使用嵌套的while
循环。但将其视为单个循环可能更容易。对于每一行,您可能只需要做三件事:
- 如果 是 没有行,因为你在 EOF,
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:
而不是重复执行 f.readline()
并检查的 while
循环。
- 使用
groupby
将行迭代器转换为以空行分隔的行组迭代器。
如果您仍然为此苦苦挣扎,这里有一个使用 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
分组分开。
您文件中块的模式是它们由以空行或文件末尾终止的行组组成。这个逻辑可以封装在一个生成器函数中,该函数迭代地从您的文件中生成行块,这将简化脚本的其余部分。
下面的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
我正在做一个项目,我想在其中使用 Python 解析文本文件。该文件由一些格式不同的块的数据条目组成。有新行时会找到新条目。这就是我想要完成的:
- 跳过前几行(前 16 行)
- 第16行后,有一个换行符开始新的数据输入
- 阅读以下行,直到遇到新的换行符。每行都附加到名为数据的列表中。
- 该列表将被传递给处理进一步处理的函数。
- 重复步骤 3 和 4,直到文件中没有更多数据
以下是文件示例:
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",它充当 "while not end of file"。我尝试在跳过前 16 行后添加 "while True"。 我对Python的解析能力知之甚少。
在此先感谢您的帮助。
在初始跳过后添加一个 while True
绝对有效。当然,你必须把所有的细节都弄好。
您可以尝试扩展您已有的方法,在外循环中使用嵌套的while
循环。但将其视为单个循环可能更容易。对于每一行,您可能只需要做三件事:
- 如果 是 没有行,因为你在 EOF,
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:
而不是重复执行f.readline()
并检查的while
循环。 - 使用
groupby
将行迭代器转换为以空行分隔的行组迭代器。
如果您仍然为此苦苦挣扎,这里有一个使用 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
分组分开。
您文件中块的模式是它们由以空行或文件末尾终止的行组组成。这个逻辑可以封装在一个生成器函数中,该函数迭代地从您的文件中生成行块,这将简化脚本的其余部分。
下面的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