【问题标题】:Read file in chunks - RAM-usage, reading strings from binary files分块读取文件 - RAM 使用,从二进制文件中读取字符串
【发布时间】:2013-06-08 00:39:16
【问题描述】:

我想了解在 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"。使用这种方法不可能检测到任何片段中的整个字符串。

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

【问题讨论】:

  • 你可以把第一个片段写成for chunk in iter(partial(f.read, chunk_size), b""): process_data(chunk)(假设是二进制模式)。最后一个问题的答案是肯定的:只需检查块是否以字符串的任何前缀结尾,下一个块是否以相应的后缀开头。
  • 感谢您提及iter - 不知道!关于第二个问题:您的意思是我可以检查文章是否以TThThiThis 结尾- 等等?嗯,好主意!谢谢!

标签: python string ram


【解决方案1】:

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()

【讨论】:

  • 感谢您的回答!我知道第一个版本更好地重用,它定义了一个在其他项目中也可能有用的函数。我猜这会导致更大的“调用堆栈空间”?创建一个函数?但是文件本身的RAM使用没有区别?我找到了一些关于生成器函数的文档,当你一直在考虑常用函数时,这并不容易理解 - 但如果我做对了,第一个版本将 return 只是文件的第一部分并且for-loop 将循环通过piece 的数据,没有yield?
  • 如果你喜欢我的答案,你能把它标记为接受的答案吗? (实际上你会得到 2 个代表)
【解决方案2】:

从 python 3.8 开始,您还可以使用 assignment expression(海象运算符):

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

最后一个chunk 可能小于CHUNK_SIZE

因为read() 将在读取文件时返回b"",所以while 循环将终止。

【讨论】:

  • 感谢您提供此信息!我需要查找这个“海象运算符”,了解更多信息可能会有所帮助。
【解决方案3】:

我认为最好和最惯用的方法可能是使用内置的 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()函数的官方文档中作为示例显示。

【讨论】:

    猜你喜欢
    • 2014-06-18
    • 2012-09-05
    • 1970-01-01
    • 1970-01-01
    • 2020-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多