Python 如何循环使用同一打开文件的两个生成器
我有一个中等大小的文件(25MB,1000000行),我想读取除第三行之外的每一行 第一个问题:将整个文件加载到内存中,然后读取行(方法Python 如何循环使用同一打开文件的两个生成器,python,generator,itertools,Python,Generator,Itertools,我有一个中等大小的文件(25MB,1000000行),我想读取除第三行之外的每一行 第一个问题:将整个文件加载到内存中,然后读取行(方法.read()),还是一次加载并读取一行(方法.readline()) 由于我不是一名经验丰富的程序员,我尝试了itertools模块中的islice方法的第二个选项 import intertools with open(input_file) as inp: inp_atomtype = itertools.islice(inp, 0, 40, 3
.read()
),还是一次加载并读取一行(方法.readline()
)
由于我不是一名经验丰富的程序员,我尝试了itertools
模块中的islice
方法的第二个选项
import intertools
with open(input_file) as inp:
inp_atomtype = itertools.islice(inp, 0, 40, 3)
inp_atomdata = itertools.islice(inp, 1, 40, 3)
for atomtype, atomdata in itertools.zip_longest(inp_atomtype, inp_atomdata):
print(atomtype + atomdata)
虽然通过单个生成器(inp_atomtype
或inp_atomdata
)循环打印正确的数据,但同时通过这两个生成器循环(如本代码所示)打印错误的数据
第二个问题:如何使用生成器到达所需的行?您不需要对迭代器进行切片,一个简单的行计数器就足够了:
with open(input_file) as f:
current_line = 0
for line in f:
current_line += 1
if current_line % 3: # ignore every third line
print(line) # NOTE: print() will add an additional new line by default
至于把它变成一个生成器,只需产生行,而不是打印
说到速度,考虑到您无论如何都要读取行,I/O部分可能会采取相同的措施,但您可能会受益一点(总处理时间)如果您有足够的工作内存来保存文件内容,并且可以预先加载整个文件而不是流式处理,则可以使用快速列表切片而不是计算行数。q2:这是我的生成器:
def yield_from_file(input_file):
with open(input_file) as file:
yield from file
def read_two_skip_one(gen):
while True:
try:
val1 = next(gen)
val2 = next(gen)
yield val1, val2
_ = next(gen)
except StopIteration:
break
if __name__ == '__main__':
for atomtype, atomdata in read_two_skip_one(yield_from_file('sample.txt')):
print(atomtype + atomdata)
sample.txt是用一个bash shell生成的(它只是一行,计数到100)
关于q1:如果要多次读取该文件,最好将其保存在内存中。否则你可以逐行阅读
关于您遇到的错误结果问题:
两个itertools.islice(inp,0,40,3)
语句都将inp
用作生成器。两者都将调用next(inp)
,为您提供一个值。
每次在迭代器上调用next()
,它都会更改其状态,因此这就是问题的根源。第一个问题:我非常确定.readline()比.read()快。另外,基于我的测试的最快方法是进行裁剪,如:
with open(file, 'r') as f:
for line in f:
...
第二个问题:我对此不太确定。你可以考虑使用产量。
您可以参考以下代码片段:
def myreadlines(f, newline):
buf = ""
while True:
while newline in buf:
pos = buf.index(newline)
yield buf[:pos]
buf = buf[pos + len(newline):]
chunk = f.read(4096)
if not chunk:
# the end of file
yield buf
break
buf += chunk
with open("input.txt") as f:
for line in myreadlines(f, "{|}"):
print (line)
yield
非常适合这一点
此函数从iterable生成对,并每三个项跳过一次:
def two_thirds(seq):
_iter = iter(seq)
while True:
yield (next(_iter), next(_iter))
next(_iter)
您将丢失一半对,这意味着三分之二(范围(2))
将立即停止迭代
您还可以从中使用石斑鱼配方,并忽略生成的每个元组中的第三项:
for atomtype, atomdata, _ in grouper(lines, 3):
pass
可以使用生成器表达式:
with open(input_file, 'r') as f:
generator = (line for e, line in enumerate(f, start=1) if e % 3)
enumerate
将行号添加到每一行,而if
子句忽略可被3整除的行号(默认编号从0开始,因此必须指定start=1
,以获得所需的模式)
请记住,您只能在文件仍处于打开状态时使用生成器。听起来像是一个x y问题,您想解决什么?根据atomtype
的值,我想正确处理atomdata
变量。您可以调用next(gen)
,而无需将其分配给。
我知道,这是一个习惯:D一些linter抱怨没有使用值。这是一个简单的解决方案,我可以同时访问这两个变量。
with open(input_file, 'r') as f:
generator = (line for e, line in enumerate(f, start=1) if e % 3)