读取大文件时出现“OSError:[Errno 22]无效参数”的错误

17

我试图编写一个小脚本,打印文件的校验和(使用 https://gist.github.com/Zireael-N/ed36997fd1a967d78cb2 中的一些代码):

import sys
import os
import hashlib

file = '/Users/Me/Downloads/2017-11-29-raspbian-stretch.img'

with open(file, 'rb') as f:
    contents = f.read()
    print('SHA256 of file is %s' % hashlib.sha256(contents).hexdigest())

但是我收到了以下错误信息:
Traceback (most recent call last):
  File "checksum.py", line 8, in <module>
    contents = f.read()
OSError: [Errno 22] Invalid argument

我做错了什么?我正在使用macOS High Sierra上的Python 3。


1
无法复现。这是在尝试对任何文件获取校验和时发生,还是仅针对特定文件?您正在使用Python 2还是Python 3?为什么您的错误消息说“contents = f.read()”是第8行,而实际上它只是这里给出的代码的第6行? - jwodder
你尝试过其他文件吗?Python只是将其从操作系统得到的错误代码(EINVAL)进行翻译,有可能该错误代码来自于文件系统驱动程序本身(因此可能是其中的一个错误)。通常情况下,对于读取操作而言,EINVAL 表示 fd 号码错误,但这是不寻常的情况,因为 Python 自己控制 fd 号码的正确性。 - myaut
1
@roganjosh:那里给出的答案仅适用于Windows系统。这个问题似乎是关于macOS上的问题。 - jwodder
1
我现在尝试了其他文件,它们都可以正常工作。然而原始的.img文件仍然会出现相同的错误信息。这可能是因为它的大小达到了4.92 GB吗? - Hallvard
2
@Hallvard:你使用的是哪个版本的Python?你的系统是32位的吗?这两个问题的答案可能会导致一些问题。 - ShadowRanger
显示剩余6条评论
2个回答

16

在 Python 的历史中有过 几个 问题(大多数在最近的版本中得到修复),其中之一是一次从文件句柄读取超过 2-4 GB 的数据(在 Python 的 32 位版本中无法修复这个问题,因为它们缺乏虚拟地址空间来分配缓冲区;尽管与 I/O 无关,但在读取大文件时最常见)。可用于哈希的解决方法是将哈希更新为固定大小的块(这本身就是一个好主意,因为指望 RAM 大于文件大小是个不好的想法)。最简单的方法是将代码更改为:

with open(file, 'rb') as f:
    hasher = hashlib.sha256()  # Make empty hasher to update piecemeal
    while True:
        block = f.read(64 * (1 << 20)) # Read 64 MB at a time; big, but not memory busting
        if not block:  # Reached EOF
            break
        hasher.update(block)  # Update with new block
print('SHA256 of file is %s' % hasher.hexdigest())  # Finalize to compute digest

如果您感觉自己很高级,可以使用两个参数的iter和一些functools魔法"简化"循环,用如下代码替换while循环即可:
for block in iter(functools.partial(f.read, 64 * (1 << 20)), b''):
    hasher.update(block)

或者在Python 3.8+中,使用海象运算符:=更简单,无需导入或难以理解的代码:

while block := f.read(64 * (1 << 20)):  # Assigns and tests result in conditional!
    hasher.update(block)

哇,太完美了。谢谢! - Hallvard

-3

哇,这可以简单多了。只需逐行读取文件:

with open('big-file.txt') as f:
  for i in f:
    print(i)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接