Wie lese ich in Python eine Binärdatei ein und führe eine Schleife über jedes Byte dieser Datei?
Antworten
Zu viele Anzeigen?Python 2.4 und frühere Versionen
f = open("myfile", "rb")
try:
byte = f.read(1)
while byte != "":
# Do stuff with byte.
byte = f.read(1)
finally:
f.close()
Python 2.5-2.7
with open("myfile", "rb") as f:
byte = f.read(1)
while byte != "":
# Do stuff with byte.
byte = f.read(1)
Beachten Sie, dass die with-Anweisung in Python-Versionen unter 2.5 nicht verfügbar ist. Um sie in Version 2.5 zu verwenden, müssen Sie sie importieren:
from __future__ import with_statement
In 2.6 ist dies nicht erforderlich.
Python 3
In Python 3 ist das ein bisschen anders. Wir erhalten nicht mehr rohe Zeichen aus dem Stream im Byte-Modus, sondern Byte-Objekte, daher müssen wir die Bedingung ändern:
with open("myfile", "rb") as f:
byte = f.read(1)
while byte != b"":
# Do stuff with byte.
byte = f.read(1)
Oder wie benhoyt sagt, überspringen Sie das nicht gleich und nutzen Sie die Tatsache, dass b""
wird als false ausgewertet. Dies macht den Code ohne Änderungen zwischen 2.6 und 3.x kompatibel. Außerdem müssten Sie die Bedingung nicht ändern, wenn Sie vom Byte-Modus zum Textmodus oder umgekehrt wechseln.
with open("myfile", "rb") as f:
byte = f.read(1)
while byte:
# Do stuff with byte.
byte = f.read(1)
python 3.8
Von nun an kann der obige Code dank des := Operators kürzer geschrieben werden.
with open("myfile", "rb") as f:
while (byte := f.read(1)):
# Do stuff with byte.
Dieser Generator liefert Bytes aus einer Datei, indem er die Datei in Stücken liest:
def bytes_from_file(filename, chunksize=8192):
with open(filename, "rb") as f:
while True:
chunk = f.read(chunksize)
if chunk:
for b in chunk:
yield b
else:
break
# example:
for b in bytes_from_file('filename'):
do_stuff_with(b)
Siehe die Python-Dokumentation für Informationen über Iteratoren y Stromerzeuger .
Wenn die Datei nicht so groß ist, dass es ein Problem darstellt, sie im Speicher zu halten:
with open("filename", "rb") as f:
bytes_read = f.read()
for b in bytes_read:
process_byte(b)
wobei process_byte für eine Operation steht, die Sie mit dem übergebenen Byte durchführen wollen.
Wenn Sie ein Stück auf einmal verarbeiten wollen:
with open("filename", "rb") as f:
bytes_read = f.read(CHUNKSIZE)
while bytes_read:
for b in bytes_read:
process_byte(b)
bytes_read = f.read(CHUNKSIZE)
En with
Anweisung ist in Python 2.5 und höher verfügbar.
Lesen einer Binärdatei in Python und Schleifenbildung über jedes Byte
Neu in Python 3.5 ist die pathlib
Modul, das eine praktische Methode zum Einlesen einer Datei in Form von Bytes enthält, so dass wir über die Bytes iterieren können. Ich halte dies für eine anständige (wenn auch schnelle und schmutzige) Antwort:
import pathlib
for byte in pathlib.Path(path).read_bytes():
print(byte)
Interessant, dass dies die einzige Antwort ist, die erwähnt wird pathlib
.
In Python 2 würden Sie dies wahrscheinlich tun (wie Vinay Sajip auch vorschlägt):
with open(path, 'b') as file:
for byte in file.read():
print(byte)
Wenn die Datei zu groß ist, um sie im Speicher zu durchlaufen, können Sie sie idiomatisch mit der Option iter
Funktion mit der callable, sentinel
Signatur - die Python 2-Version:
with open(path, 'b') as file:
callable = lambda: file.read(1024)
sentinel = bytes() # or b''
for chunk in iter(callable, sentinel):
for byte in chunk:
print(byte)
(Mehrere andere Antworten erwähnen dies, aber nur wenige bieten eine vernünftige Lesegröße).
Bewährte Verfahren für große Dateien oder gepuffertes/interaktives Lesen
Lassen Sie uns eine Funktion erstellen, die dies tut, einschließlich der idiomatischen Verwendung der Standardbibliothek für Python 3.5+:
from pathlib import Path
from functools import partial
from io import DEFAULT_BUFFER_SIZE
def file_byte_iterator(path):
"""given a path, return an iterator over the file
that lazily loads the file
"""
path = Path(path)
with path.open('rb') as file:
reader = partial(file.read1, DEFAULT_BUFFER_SIZE)
file_iterator = iter(reader, bytes())
for chunk in file_iterator:
yield from chunk
Beachten Sie, dass wir file.read1
. file.read
blockiert, bis er alle angeforderten Bytes erhält oder EOF
. file.read1
ermöglicht es uns, eine Blockierung zu vermeiden, und sie kann deshalb schneller zurückkehren. In keiner anderen Antwort wird dies erwähnt.
Demonstration der Anwendung bewährter Verfahren:
Erstellen wir eine Datei mit einem Megabyte (eigentlich Mebibyte) Pseudozufallsdaten:
import random
import pathlib
path = 'pseudorandom_bytes'
pathobj = pathlib.Path(path)
pathobj.write_bytes(
bytes(random.randint(0, 255) for _ in range(2**20)))
Lassen Sie uns nun darüber iterieren und es im Speicher materialisieren:
>>> l = list(file_byte_iterator(path))
>>> len(l)
1048576
Wir können jeden Teil der Daten untersuchen, zum Beispiel die letzten 100 und die ersten 100 Bytes:
>>> l[-100:]
[208, 5, 156, 186, 58, 107, 24, 12, 75, 15, 1, 252, 216, 183, 235, 6, 136, 50, 222, 218, 7, 65, 234, 129, 240, 195, 165, 215, 245, 201, 222, 95, 87, 71, 232, 235, 36, 224, 190, 185, 12, 40, 131, 54, 79, 93, 210, 6, 154, 184, 82, 222, 80, 141, 117, 110, 254, 82, 29, 166, 91, 42, 232, 72, 231, 235, 33, 180, 238, 29, 61, 250, 38, 86, 120, 38, 49, 141, 17, 190, 191, 107, 95, 223, 222, 162, 116, 153, 232, 85, 100, 97, 41, 61, 219, 233, 237, 55, 246, 181]
>>> l[:100]
[28, 172, 79, 126, 36, 99, 103, 191, 146, 225, 24, 48, 113, 187, 48, 185, 31, 142, 216, 187, 27, 146, 215, 61, 111, 218, 171, 4, 160, 250, 110, 51, 128, 106, 3, 10, 116, 123, 128, 31, 73, 152, 58, 49, 184, 223, 17, 176, 166, 195, 6, 35, 206, 206, 39, 231, 89, 249, 21, 112, 168, 4, 88, 169, 215, 132, 255, 168, 129, 127, 60, 252, 244, 160, 80, 155, 246, 147, 234, 227, 157, 137, 101, 84, 115, 103, 77, 44, 84, 134, 140, 77, 224, 176, 242, 254, 171, 115, 193, 29]
Bei Binärdateien nicht nach Zeilen iterieren
Tun Sie nicht das Folgende - dies zieht einen Brocken beliebiger Größe, bis er ein Zeilenumbruchzeichen erreicht - zu langsam, wenn die Brocken zu klein sind, und möglicherweise auch zu groß:
with open(path, 'rb') as file:
for chunk in file: # text newline iteration - not for bytes
yield from chunk
Die obige Option eignet sich nur für semantisch lesbare Textdateien (wie reiner Text, Code, Markup, Markdown usw... im Wesentlichen alles, was mit ascii, utf, latin usw... kodiert ist), die Sie ohne die 'b'
Flagge.
Um eine Datei zu lesen - ein Byte nach dem anderen (ohne Berücksichtigung der Pufferung) - können Sie die Funktion zwei-Argumente iter(callable, sentinel)
eingebaute Funktion :
with open(filename, 'rb') as file:
for byte in iter(lambda: file.read(1), b''):
# Do stuff with byte
Sie fordert file.read(1)
bis es nichts mehr ergibt b''
(leerer Bytestring). Der Speicher wächst bei großen Dateien nicht unbegrenzt. Sie könnten übergeben buffering=0
a open()
um die Pufferung zu deaktivieren - sie garantiert, dass nur ein Byte pro Iteration gelesen wird (langsam).
with
-Anweisung schließt die Datei automatisch - auch dann, wenn der darunter liegende Code eine Ausnahme auslöst.
Trotz der standardmäßig vorhandenen internen Pufferung ist es ineffizient, ein Byte nach dem anderen zu verarbeiten. Zum Beispiel ist hier die blackhole.py
Nutzen, der alles frisst, was ihm gegeben wird:
#!/usr/bin/env python3
"""Discard all input. `cat > /dev/null` analog."""
import sys
from functools import partial
from collections import deque
chunksize = int(sys.argv[1]) if len(sys.argv) > 1 else (1 << 15)
deque(iter(partial(sys.stdin.detach().read, chunksize), b''), maxlen=0)
例
$ dd if=/dev/zero bs=1M count=1000 | python3 blackhole.py
Sie verarbeitet ~1,5 GB/s cuando chunksize == 32768
auf meinem Rechner und nur ~7,5 MB/s cuando chunksize == 1
. Das heißt, es ist 200 Mal langsamer, ein Byte nach dem anderen zu lesen. Berücksichtigen Sie dies, wenn Sie Ihre Verarbeitung umschreiben können, um mehr als ein Byte auf einmal zu verwenden und wenn Sie brauchen Leistung.
mmap
ermöglicht es Ihnen, eine Datei wie eine bytearray
und ein Dateiobjekt gleichzeitig. Es kann als Alternative zum Laden der gesamten Datei in den Speicher dienen, wenn Sie auf beide Schnittstellen zugreifen müssen. Insbesondere können Sie Byte für Byte über eine im Speicher abgebildete Datei iterieren, indem Sie eine einfache for
-Schleife:
from mmap import ACCESS_READ, mmap
with open(filename, 'rb', 0) as f, mmap(f.fileno(), 0, access=ACCESS_READ) as s:
for byte in s: # length is equal to the current file size
# Do stuff with byte
mmap
unterstützt die Slice-Notation. Zum Beispiel, mm[i:i+len]
gibt zurück. len
Bytes aus der Datei, beginnend an der Position i
. Das Kontextmanager-Protokoll wird vor Python 3.2 nicht unterstützt; Sie müssen mm.close()
explizit in diesem Fall. Iteration über jedes Byte mit mmap
verbraucht mehr Speicher als file.read(1)
aber mmap
ist um eine Größenordnung schneller.
- See previous answers
- Weitere Antworten anzeigen