452 Stimmen

Lesen einer Binärdatei und Schleifenbildung über jedes Byte

Wie lese ich in Python eine Binärdatei ein und führe eine Schleife über jedes Byte dieser Datei?

472voto

Skurmedel Punkte 20549

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.

192voto

codeape Punkte 93809

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 .

62voto

Vinay Sajip Punkte 89444

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.

43voto

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.

41voto

jfs Punkte 370717

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.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X