15

我想了解在 python 中读取大文件时这种方法在 RAM 使用方面的区别。

版本 1,在 stackoverflow 上找到:

def read_in_chunks(file_object, chunk_size=1024):
    while True:
        data = file_object.read(chunk_size)
        if not data:
            break
        yield data

f = open(file, 'rb')
for piece in read_in_chunks(f):
    process_data(piece)
f.close()

版本 2,我在找到上面的代码之前使用了这个:

f = open(file, 'rb')
while True:
    piece = f.read(1024)
    process_data(piece)
f.close()

该文件在两个版本中都被部分读取。并且可以处理当前的一块。在第二个示例中,piece在每个周期都获取新内容,所以我认为这可以在将完整文件加载到内存的情况下完成这项工作。

但我真的不明白是什么yield,而且我很确定我在这里做错了。谁能给我解释一下?


除了使用的方法之外,还有其他事情让我感到困惑:

我读的那篇文章的内容是由上面例子中的块大小定义的,1KB。但是...如果我需要在文件中查找字符串怎么办?像"ThisIsTheStringILikeToFind"什么?

根据字符串在文件中出现的位置,可能是一段包含该部分"ThisIsTheStr"- 下一段将包含"ingILikeToFind". 使用这种方法不可能检测到任何片段中的整个字符串。

有没有办法分块读取文件 - 但不知何故关心这些字符串?

4

3 回答 3

23

yield是python中用于生成器表达式的关键字。这意味着下次调用(或迭代)该函数时,执行将在您上次调用它时停止的确切点重新开始。这两个函数的行为相同;唯一的区别是第一个使用的调用堆栈空间比第二个多一点。然而,第一个更可重用,所以从程序设计的角度来看,第一个实际上更好。

编辑:另外,另一个区别是第一个将在所有数据都被读取后停止读取,它应该的方式,但第二个只会停止一次f.read()process_data()抛出异常。为了让第二个正常工作,您需要像这样修改它:

f = open(file, 'rb')
while True:
    piece = f.read(1024)  
    if not piece:
        break
    process_data(piece)
f.close()
于 2013-06-12T01:43:22.883 回答
8

从 python 3.8 开始,您还可以使用赋值表达式(海象运算符):

with open('file.name', 'rb') as file:
    while chunk := file.read(1024):
        process_data(chunk)

最后一个chunk可能小于CHUNK_SIZE

asread()b""在读取文件时返回,while循环将终止。

于 2020-08-24T14:41:53.373 回答
7

我认为最好和最惯用的方法可能是使用内置iter()函数及其可选sentinel参数来创建和使用可迭代对象,如下所示。请注意,如果文件大小不是它的精确倍数,则最后一个块可能小于请求的块大小。

from functools import partial

CHUNK_SIZE = 1024
filename = 'testfile.dat'

with open(filename, 'rb') as file:
    for chunk in iter(partial(file.read, CHUNK_SIZE), b''):
        process_data(chunk)

更新:不知道它是什么时候添加的,但几乎与上面的内容完全一样,现在在iter()函数的官方文档中作为示例显示。

于 2019-12-20T16:53:00.120 回答