python3 使用生成器过滤包含多行记录的文件

python3 using a generator to filter file with multiline records

我需要读取结构为多行记录的大文件并写入具有特定索引的文件记录,例如记录号 R = 1、2 和 1093。 如果每条记录有 N = 3 行,这相当于逐行读取文件,然后写入行号 1、2、3 和 4、5、6 和 3277、3278、3279(因为每条记录的第一行 Ri 开始在行号 Ri-1 * N + 1.

我想可以计算出要写入的行数,然后逐行浏览文件并写入这些行数。但是,是否可以将 "zip" 连续的第 1、2 和 3 行放入包含记录的生成器对象中并以某种方式过滤这些记录,或者如果它们枚举到 R 则将它们直接打印到文件中? 这个伪代码的一些东西:

def subset(file_in, file_out, N, R):
    with open(file_in, "rt") as fin, open(file_out, "wt") as fout:
        line = (line.rstrip() for line in fin)
        record = enumerate(zip(line, line, line)) # What if records are of size N
        for i, r in record if i in R:
            fout.write(r)

想要记录大小N作为参数怎么办?

更新示例

file_in 的示例(4 条记录,3 lines/record):

dslfkj
2
a
dflkj
3
g
fds
2
b
fsdlkj
1
n

然后 subset(file_in, file_out, 3, [1,3]) 会给出 (file_out)

dslfkj
2
a
fds
2
b

对于这个问题,使用楼层划分直接逐行解决是有意义的。

例如:

fin = '''
dslfkj
2
a
dflkj
3
g
fds
2
b
fsdlkj
1
'''

line_gen = (line.rstrip() for line in fin.strip().split())

R = [1, 3]
R = [val - 1 for val in R] #zero indexing
N = 3
for i, line in enumerate(line_gen):
    if i // N in R:
        print(line)

输出:

dslfkj
2
a
fds
2
b

您的函数可能如下所示:(您可能想检查它是否开箱即用或需要调整。我没有检查文件打开部分。

def subset(file_in, file_out, N, R):
    R = [val - 1 for val in R] #zero indexing
    with open(file_in, "rt") as fin, open(file_out, "wt") as fout:
        line_gen = (line.rstrip() for line in fin)
        for i, line in enumerate(line_gen):
            if i // N in R:
                fout.write(line)
                fout.write('\n')

编辑:下面的答案涉及如何使用生成器并将值组合在一起。话虽如此,我认为您不需要使用它。但是,如果您仍然愿意,可以基于它构建您的函数。

旧答案:

您可以使用列表创建对象的 n 引用,然后使用 * (aka splat) operator.

解包

例如:

from itertools import zip_longest
line = (x for x in range(100, 132))
n = 3
record = zip(*([line] * n)) #equivalent to *[line, line, line] which is unpacked into zip arguments
for i, r in enumerate(record):
    print(i, r)

0 (100, 101, 102)
1 (103, 104, 105)
2 (106, 107, 108)
3 (109, 110, 111)
4 (112, 113, 114)
5 (115, 116, 117)
6 (118, 119, 120)
7 (121, 122, 123)
8 (124, 125, 126)
9 (127, 128, 129)

此外,根据您希望 "leftover" 行发生的情况,您可能希望改用 zip_longest