Odczyt pliku binarnego i zapętlenie nad każdym bajtem
W Pythonie, jak odczytać plik binarny i zapętlić każdy bajt tego pliku?
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)
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 .
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()
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.
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)
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
.
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).
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
.
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.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.
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