Odczyt pliku binarnego i zapętlenie nad każdym bajtem

W Pythonie, jak odczytać plik binarny i zapętlić każdy bajt tego pliku?

Author: martineau, 2009-06-24

9 answers

f = open("myfile", "rb")
try:
    byte = f.read(1)
    while byte != "":
        # Do stuff with byte.
        byte = f.read(1)
finally:
    f.close()

Przez sugestię chrispy:

with open("myfile", "rb") as f:
    byte = f.read(1)
    while byte != "":
        # Do stuff with byte.
        byte = f.read(1)

Zauważ, że instrukcja with nie jest dostępna w wersjach Pythona poniżej 2.5. Aby użyć go w v 2.5 musisz go zaimportować:

from __future__ import with_statement

W 2.6 nie jest to potrzebne.

W Pythonie 3 jest trochę inaczej. Nie będziemy już pobierać surowych znaków ze strumienia w trybie bajtowym, ale obiektów bajtowych, dlatego musimy zmienić warunek:
with open("myfile", "rb") as f:
    byte = f.read(1)
    while byte != b"":
        # Do stuff with byte.
        byte = f.read(1)

Lub jak mówi benhoyt, pomiń nie równe i skorzystaj z faktu, że b"" ocenia NA false. Dzięki temu kod jest zgodny między 2.6 A 3.x bez żadnych zmian. To również uchroni Cię przed zmianą warunku, jeśli przejdziesz z trybu bajtowego do tekstu lub odwrotnie.

with open("myfile", "rb") as f:
    byte = f.read(1)
    while byte:
        # Do stuff with byte.
        byte = f.read(1)
 294
Author: Skurmedel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-23 10:48:16

Ten generator pobiera bajty z pliku, odczytując plik w kawałkach:

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)

Zobacz dokumentację Pythona, aby uzyskać informacje na temat iteratorów I GENERATORÓW .

 138
Author: codeape,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-17 23:31:16

Jeśli plik nie jest zbyt duży, to trzymanie go w pamięci jest problemem:

bytes_read = open("filename", "rb").read()
for b in bytes_read:
    process_byte(b)

Gdzie process_byte reprezentuje operację, którą chcesz wykonać na bajcie przekazanym.

Jeśli chcesz przetworzyć kawałek na raz:

file = open("filename", "rb")
try:
    bytes_read = file.read(CHUNKSIZE)
    while bytes_read:
        for b in bytes_read:
            process_byte(b)
        bytes_read = file.read(CHUNKSIZE)
finally:
    file.close()
 44
Author: Vinay Sajip,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-23 21:43:03

Aby odczytać plik-jeden bajt na raz ( ignorując buforowanie) - możesz użyć dwuargumentowej iter(callable, sentinel) wbudowanej funkcji :

with open(filename, 'rb') as file:
    for byte in iter(lambda: file.read(1), b''):
        # Do stuff with byte

Wywołuje file.read(1) dopóki nie zwróci niczego b'' (pusty bajt). Pamięć nie rośnie Nieograniczona dla dużych plików. Można przekazać buffering=0 do open(), aby wyłączyć buforowanie-gwarantuje to, że tylko jeden bajt jest odczytywany na iteracji (wolno).

with-Instrukcja zamyka plik automatycznie-w tym przypadku, gdy kod pod spodem pojawia się wyjątek.

Pomimo domyślnego buforowania wewnętrznego, wciąż nieefektywne jest przetwarzanie jednego bajtu na raz. Na przykład, oto blackhole.py narzędzie, które zjada wszystko, co jest podane:

#!/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)

Przykład:

$ dd if=/dev/zero bs=1M count=1000 | python3 blackhole.py

Przetwarza ~1.5 GB/s kiedy chunksize == 32768 na mojej maszynie i tylko ~7.5 MB/s kiedy chunksize == 1. Oznacza to, że odczyt po jednym bajcie jest 200 razy wolniejszy. Weź to pod uwagę, jeśli możesz przepisać przetwarzanie aby użyć więcej niż jednego bajtu na raz i jeśli potrzebujesz wydajności.

mmap pozwala traktować plik jako bytearray i jednocześnie obiekt file. Może służyć jako alternatywa dla ładowania całego pliku w pamięci, jeśli potrzebujesz dostępu do obu interfejsów. W szczególności, można iterować jeden bajt na raz przez plik mapowany pamięcią, używając zwykłej pętli for-loop:

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 obsługuje notację slice. Na przykład, mm[i:i+len] zwraca len bajtów z pliku zaczynającego się na pozycji i. Protokół menedżera kontekstu nie jest obsługiwany przed Pythonem 3.2; w tym przypadku musisz jawnie wywołać mm.close(). Iteracja nad każdym bajtem przy użyciu mmap zużywa więcej pamięci niż file.read(1), ale mmap jest o rząd wielkości szybsza.

 26
Author: jfs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-19 07:12:49

Podsumowując wszystkie genialne punkty chrispy, Skurmedel, Ben Hoyt i Peter Hansen, byłoby to optymalne rozwiązanie do przetwarzania pliku binarnego jeden bajt na raz:

with open("myfile", "rb") as f:
    while True:
        byte = f.read(1)
        if not byte:
            break
        do_stuff_with(ord(byte))

Dla Pythona w wersji 2.6 i nowszej, ponieważ:

  • bufory Pythona wewnętrznie - nie trzeba czytać fragmentów
  • zasada suchości - nie powtarzaj odczytywanej linii
  • z instrukcją zapewnia czyste zamknięcie pliku
  • ' byte ' ocenia NA false, gdy nie ma więcej bajtów (Nie gdy bajt jest zero)

Lub użyj rozwiązania J. F. Sebastians dla poprawy prędkości

from functools import partial

with open(filename, 'rb') as file:
    for byte in iter(partial(file.read, 1), b''):
        # Do stuff with byte

Lub jeśli chcesz, aby była to funkcja generatora, jak zademonstrowana przez codeape:

def bytes_from_file(filename):
    with open(filename, "rb") as f:
        while True:
            byte = f.read(1)
            if not byte:
                break
            yield(ord(byte))

# example:
for b in bytes_from_file('filename'):
    do_stuff_with(b)
 18
Author: Holger Bille,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-09 08:16:27

Odczyt pliku binarnego w Pythonie i zapętlenie każdego bajtu

Nowością w Pythonie 3.5 jest moduł pathlib, który ma wygodną metodę odczytu w pliku jako bajty, co pozwala nam na iterację nad bajtami. Jest to jedna z najbardziej rozpoznawalnych i najbardziej rozpoznawalnych marek w Polsce.]}

import pathlib

for byte in pathlib.Path(path).read_bytes():
    print(byte)

Interesujące, że jest to jedyna odpowiedź, o której można wspomnieć pathlib.

W Pythonie 2 prawdopodobnie zrobiłbyś to (jak sugeruje również Vinay Sajip):]}
with open(path, 'b') as file:
    for byte in file.read():
        print(byte)

W przypadku, gdy plik może być zbyt duży, aby można go było iterować w pamięci, idiomatycznie, używając funkcji iter z sygnaturą callable, sentinel-wersja Pythona 2:

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)

(kilka innych odpowiedzi mówi o tym, ale niewiele oferuje rozsądny rozmiar odczytu.)

Najlepsze praktyki dla dużych plików lub odczytu buforowanego / interaktywnego

Stwórzmy funkcję do tego celu, włączając w to idiomatyczne zastosowania standardowej biblioteki Pythona 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:
            for byte in chunk:
                yield byte

Zauważ, że używamy file.read1. file.read blokuje, dopóki nie otrzyma wszystkich żądanych bajtów lub EOF. file.read1 pozwala nam uniknąć blokowania i może z tego powodu powrócić szybciej. Żadne inne odpowiedzi również o tym nie wspominają.

Demonstracja stosowania najlepszych praktyk:

Zróbmy Plik z megabajtem (właściwie mebibajtem) pseudorandomowych danych:

import random
import pathlib
path = 'pseudorandom_bytes'
pathobj = pathlib.Path(path)

pathobj.write_bytes(
  bytes(random.randint(0, 255) for _ in range(2**20)))

Teraz powtórzmy to i zmaterializujmy w pamięci:

>>> l = list(file_byte_iterator(path))
>>> len(l)
1048576

Możemy sprawdzić dowolną część danych, na przykład ostatnie 100 i pierwsze 100 bajtów:

>>> 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]

Nie iteruj według linii dla plików binarnych

Nie wykonuj następujących czynności-to pociąga kawałek dowolnego rozmiaru, aż do uzyskania znaku nowej linii - zbyt wolno, gdy kawałki są zbyt małe, a być może zbyt duże]}

    with open(path, 'rb') as file:
        for chunk in file: # text newline iteration - not for bytes
            for byte in chunk:
                yield byte

Powyższe jest dobre tylko dla tego, co są semantycznie czytelne dla człowieka pliki tekstowe (jak zwykły tekst, kod, znaczniki, markdown itp... zasadniczo wszystko ascii, utf, latin itp... kodowane).

 10
Author: Aaron Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-09 17:58:01

Python 3, Przeczytaj cały plik na raz:

with open("filename", "rb") as binary_file:
    # Read the whole file at once
    data = binary_file.read()
    print(data)

Możesz iterować co chcesz używając zmiennej data.

 3
Author: Mircea,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-25 05:47:20

Jeśli masz dużo danych binarnych do odczytania, warto rozważyć Moduł struct . Jest to udokumentowane jako konwersja "między typami C i Python", ale oczywiście bajty są bajtami i to, czy zostały utworzone jako typy C, nie ma znaczenia. Na przykład, jeśli Twoje dane binarne zawierają dwie 2-bajtowe liczby całkowite i jedną 4-bajtową liczbę całkowitą, możesz je odczytać w następujący sposób (przykład zaczerpnięty z Dokumentacji struct):

>>> struct.unpack('hhl', b'\x00\x01\x00\x02\x00\x00\x00\x03')
(1, 2, 3)
Może to być wygodniejsze, szybsze lub jedno i drugie, niż jawne zapętlenie zawartości pliku.
 1
Author: gerrit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-01 11:24:30

Jeśli szukasz czegoś szybkiego, oto metoda, której używam, która działa od lat:

from array import array

with open( path, 'rb' ) as file:
    data = array( 'B', file.read() ) # buffer the file

# evaluate it's data
for byte in data:
    v = byte # int value
    c = chr(byte)

Jeśli chcesz iterować znaki zamiast ints, możesz po prostu użyć data = file.read(), który powinien być obiektem bytes () w py3.

 0
Author: Tcll,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-23 12:30:52