txt sobreescribir open leer formas contenido como columnas binarios archivos archivo python file-io binary

sobreescribir - leer un archivo en python 3



Leyendo archivo binario y repitiendo sobre cada byte (9)

Leyendo un archivo binario en Python y haciendo un bucle sobre cada byte

Lo nuevo en Python 3.5 es el módulo pathlib , que tiene un método de conveniencia específicamente para leer un archivo como bytes, lo que nos permite iterar sobre los bytes. Considero que esta es una respuesta decente (aunque rápida y sucia):

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

Es interesante que esta es la única respuesta para mencionar pathlib .

En Python 2, probablemente harías esto (como sugiere Vinay Sajip):

with open(path, ''b'') as file: for byte in file.read(): print(byte)

En el caso de que el archivo sea demasiado grande para iterarlo en la memoria, lo fragmentaría, idiomáticamente, usando la función iter con la firma callable, sentinel la versión de Python 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)

(Varias otras respuestas mencionan esto, pero pocas ofrecen un tamaño de lectura razonable).

Mejores prácticas para archivos grandes o lectura interactiva / almacenada en búfer

Vamos a crear una función para hacer esto, incluidos los usos idiomáticos de la biblioteca estándar para 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: for byte in chunk: yield byte

Tenga en cuenta que usamos file.read1 . file.read bloquea hasta que obtiene todos los bytes solicitados de él o EOF . file.read1 nos permite evitar el bloqueo y puede regresar más rápidamente debido a esto. Ninguna otra respuesta menciona esto también.

Demostración del uso de las mejores prácticas:

Hagamos un archivo con un megabyte (en realidad mebibyte) de datos pseudoaleatorios:

import random import pathlib path = ''pseudorandom_bytes'' pathobj = pathlib.Path(path) pathobj.write_bytes( bytes(random.randint(0, 255) for _ in range(2**20)))

Ahora vamos a iterar sobre él y materializarlo en la memoria:

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

Podemos inspeccionar cualquier parte de los datos, por ejemplo, los últimos 100 y los primeros 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]

No iterar por líneas para archivos binarios

No haga lo siguiente: esto extrae un trozo de tamaño arbitrario hasta que llega a un carácter de nueva línea, demasiado lento cuando los trozos son demasiado pequeños y posiblemente demasiado grandes también:

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

Lo anterior solo es válido para lo que son archivos de texto legibles semánticamente humanos (como texto sin formato, código, marcado, marcado, etc.) esencialmente cualquier cosa ASCII, utf, latin, etc ... codificada.

En Python, ¿cómo leo un archivo binario y recorro cada byte de ese archivo?


Este generador produce bytes de un archivo, leyendo el archivo en trozos:

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)

Consulte la documentación de Python para obtener información sobre iterators y generators .


Para leer un archivo, un byte a la vez (ignorando el almacenamiento en búfer), puede usar la función incorporada iter(callable, sentinel) dos argumentos :

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

Llama a file.read(1) hasta que no devuelve nada b'''' (bytestring vacío). La memoria no crece ilimitada para archivos grandes. Podría pasar el buffering=0 para open() , deshabilitar el búfer; garantiza que solo se lea un byte por iteración (lento).

with -statement cierra el archivo automáticamente, incluido el caso en el que el código debajo genera una excepción.

A pesar de la presencia de almacenamiento en búfer interno por defecto, todavía es ineficiente procesar un byte a la vez. Por ejemplo, aquí está la utilidad blackhole.py que come todo lo que se le da:

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

Ejemplo:

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

Procesa ~ 1.5 GB / s cuando chunksize == 32768 en mi máquina y solo ~ 7.5 MB / s cuando chunksize == 1 . Es decir, es 200 veces más lento leer un byte a la vez. Téngalo en cuenta si puede reescribir su procesamiento para usar más de un byte a la vez y si necesita rendimiento.

mmap permite tratar un archivo como un bytearray y un objeto de archivo simultáneamente. Puede servir como una alternativa para cargar todo el archivo en la memoria si necesita acceder a ambas interfaces. En particular, puede iterar un byte a la vez sobre un archivo asignado en memoria simplemente usando un plano 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 soporta la notación de corte. Por ejemplo, mm[i:i+len] devuelve len bytes del archivo que comienza en la posición i . El protocolo del administrador de contexto no es compatible antes de Python 3.2; necesita llamar a mm.close() explícitamente en este caso. Iterar sobre cada byte usando mmap consume más memoria que file.read(1) , pero mmap es un orden de magnitud más rápido.


Para resumir todos los puntos brillantes de Chrispy, Skurmedel, Ben Hoyt y Peter Hansen, esta sería la solución óptima para procesar un archivo binario de un byte a la vez:

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

Para las versiones 2.6 y superiores de python, porque:

  • amortiguadores de pitón internamente - no hay necesidad de leer fragmentos
  • Principio DRY - no repetir la línea de lectura
  • con declaración asegura un cierre de archivo limpio
  • ''byte'' se evalúa como falso cuando no hay más bytes (no cuando un byte es cero)

O usa la solución JF Sebastians para mejorar la velocidad

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

O si lo quieres como una función generadora como la demostrada por 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)


Python 3, lee todo el archivo a la vez:

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

Puedes iterar lo que quieras usando data variable de data .


Si el archivo no es demasiado grande, mantenerlo en la memoria es un problema:

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

donde process_byte representa alguna operación que desea realizar en el byte pasado.

Si quieres procesar un trozo a la vez:

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()


Si está buscando algo rápido, aquí hay un método que he estado usando durante años:

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)

Si desea iterar caracteres en lugar de ints, simplemente puede usar data = file.read() , que debería ser un objeto bytes () en py3.


Si tiene una gran cantidad de datos binarios para leer, puede considerar el módulo de estructura . Está documentado como la conversión de "entre los tipos C y Python", pero, por supuesto, los bytes son bytes, y no importa si esos se crearon como tipos C. Por ejemplo, si sus datos binarios contienen dos enteros de 2 bytes y un entero de 4 bytes, puede leerlos de la siguiente manera (ejemplo tomado de la documentación de la struct ):

>>> struct.unpack(''hhl'', b''/x00/x01/x00/x02/x00/x00/x00/x03'') (1, 2, 3)

Puede encontrar esto más conveniente, más rápido, o ambos, que hacer un bucle explícito sobre el contenido de un archivo.


Python 2.4 y anteriores

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)

Tenga en cuenta que la instrucción with no está disponible en versiones de Python por debajo de 2.5. Para usarlo en v 2.5 necesitarás importarlo:

from __future__ import with_statement

En 2.6 esto no es necesario.

Python 3

En Python 3, es un poco diferente. Ya no obtendremos caracteres en bruto del flujo en modo byte, sino objetos de bytes, por lo tanto, necesitamos modificar la condición:

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

O como dice Benhoyt, omita lo que no es igual y aproveche el hecho de que b"" evalúa como falso. Esto hace que el código sea compatible entre 2.6 y 3.x sin ningún cambio. También le evitaría cambiar la condición si pasa del modo de byte al texto o al revés.

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