web-dev-qa-db-fra.com

"OSError: [Errno 22] Argument invalide" lors de la lecture () d'un fichier énorme

J'essaie d'écrire un petit script qui imprime la somme de contrôle d'un fichier (en utilisant du code de 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())

Mais je reçois le message d'erreur suivant:

Traceback (most recent call last):
  File "checksum.py", line 8, in <module>
    contents = f.read()
OSError: [Errno 22] Invalid argument

Qu'est-ce que je fais mal? J'utilise python 3 sur macOS High Sierra

12
Hallvard

Il y a ont étéplusieursproblèmes au cours de l'histoire de Python (le plus corrigé dans les versions récentes) lisant plus de 2 à 4 Go à la fois à partir d'un descripteur de fichier (une version non réparable du problème se produit également sur les versions 32 bits de Python, où il leur manque simplement l'espace d'adressage virtuel pour allouer le tampon; pas lié aux E/S, mais vu le plus souvent slurping fichiers de grande taille.) Une solution de contournement disponible pour le hachage consiste à mettre à jour le hachage en morceaux de taille fixe (ce qui est une bonne idée de toute façon, car compter sur RAM étant plus grand que la taille du fichier est une mauvaise idée). L'approche la plus simple consiste à modifier votre code pour:

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

Si vous en avez envie, vous pouvez "simplifier" la boucle en utilisant la magie à deux arguments iter et un peu de functools, en remplaçant l'ensemble de la boucle while par:

for block in iter(functools.partial(f.read, 64 * (1 << 20)), b''):
    hasher.update(block)
10
ShadowRanger