txt - ¿Método perezoso para leer archivos grandes en Python?
leer una linea especifica de un archivo en python (11)
Tengo un archivo muy grande de 4GB y cuando intento leerlo, mi computadora se cuelga. Así que quiero leerlo pieza por pieza y luego de procesar cada pieza, almacene la pieza procesada en otro archivo y lea la siguiente pieza.
¿Hay algún método para yield
estas piezas?
Me encantaría tener un método perezoso .
Creo que podemos escribir así:
def read_file(path, block_size=1024):
with open(path, ''rb'') as f:
while True:
piece = f.read(block_size)
if piece:
yield piece
else:
return
for piece in read_file(path):
process_piece(piece)
Eche un vistazo a esta publicación en Neopythonic : "Clasificación de un millón de enteros de 32 bits en 2 MB de RAM usando Python"
Estoy en una situación algo similar. No está claro si conoces el tamaño del fragmento en bytes; Normalmente no lo hago, pero se conoce el número de registros (líneas) que se requiere:
def get_line():
with open(''4gb_file'') as file:
for i in file:
yield i
lines_required = 100
gen = get_line()
chunk = [i for i, j in zip(gen, range(lines_required))]
Actualización : gracias nosklo. Esto es lo que quise decir. Casi funciona, excepto que pierde una línea ''entre'' trozos.
chunk = [next(gen) for i in range(lines_required)]
Hace el truco sin perder ninguna línea, pero no se ve muy bien.
No se me permite comentar debido a mi baja reputación, pero la solución SilentGhosts debería ser mucho más fácil con file.readlines ([sizehint])
edición: SilentGhost tiene razón, pero esto debería ser mejor que:
s = ""
for i in xrange(100):
s += file.next()
Para escribir una función perezosa, solo usa yield
:
def read_in_chunks(file_object, chunk_size=1024):
"""Lazy function (generator) to read a file piece by piece.
Default chunk size: 1k."""
while True:
data = file_object.read(chunk_size)
if not data:
break
yield data
f = open(''really_big_file.dat'')
for piece in read_in_chunks(f):
process_data(piece)
Otra opción sería usar iter
y una función auxiliar:
f = open(''really_big_file.dat'')
def read1k():
return f.read(1024)
for piece in iter(read1k, ''''):
process_data(piece)
Si el archivo está basado en líneas, el objeto de archivo ya es un generador de líneas perezoso:
for line in open(''really_big_file.dat''):
process_data(line)
Para procesar línea por línea, esta es una solución elegante:
def stream_lines(file_name):
file = open(file_name)
while True:
line = file.readline()
if not line:
file.close()
break
yield line
Mientras no haya líneas en blanco.
Puedes usar el siguiente código.
file_obj = open(''big_file'')
open () devuelve un objeto de archivo
luego usa os.stat para obtener el tamaño
file_size = os.stat(''big_file'').st_size
for i in range( file_size/1024):
print file_obj.read(1024)
Ya hay muchas respuestas buenas, pero recientemente tuve un problema similar y la solución que necesitaba no se encuentra aquí, así que pensé que podría complementar este hilo.
El 80% del tiempo, necesito leer los archivos línea por línea. Luego, como se sugiere en esta answer , desea utilizar el objeto de archivo como un generador perezoso:
with open(''big.csv'') as f:
for line in f:
process(line)
Sin embargo, recientemente me topé con un csv muy grande (casi) de una sola línea, donde el separador de fila no era, en realidad, ''/n''
sino ''|''
.
- La lectura línea por línea no era una opción, pero aún necesitaba procesarla fila por fila.
- Convertir
''|''
a''/n''
antes del procesamiento también estaba fuera de cuestión, porque algunos de los campos de este csv contenían''/n''
(entrada de usuario de texto libre). - El uso de la biblioteca csv también se descartó porque el hecho de que, al menos en las primeras versiones de la biblioteca, es difícil leer la línea de entrada por línea .
Se me ocurrió el siguiente fragmento:
def rows(f, chunksize=1024, sep=''|''):
"""
Read a file where the row separator is ''|'' lazily.
Usage:
>>> with open(''big.csv'') as f:
>>> for r in rows(f):
>>> process(row)
"""
incomplete_row = None
while True:
chunk = f.read(chunksize)
if not chunk: # End of file
if incomplete_row is not None:
yield incomplete_row
break
# Split the chunk as long as possible
while True:
i = chunk.find(sep)
if i == -1:
break
# If there is an incomplete row waiting to be yielded,
# prepend it and set it back to None
if incomplete_row is not None:
yield incomplete_row + chunk[:i]
incomplete_row = None
else:
yield chunk[:i]
chunk = chunk[i+1:]
# If the chunk contained no separator, it needs to be appended to
# the current incomplete row.
if incomplete_row is not None:
incomplete_row += chunk
else:
incomplete_row = chunk
Lo he probado con éxito en archivos grandes y con diferentes tamaños de trozos (incluso probé un tamaño de trozos de 1 byte, solo para asegurarme de que el algoritmo no depende del tamaño).
file.readlines () toma un argumento de tamaño opcional que se aproxima al número de líneas leídas en las líneas devueltas.
bigfile = open(''bigfilename'',''r'')
tmp_lines = bigfile.readlines(BUF_SIZE)
while tmp_lines:
process([line for line in tmp_lines])
tmp_lines = bigfile.readlines(BUF_SIZE)
Si su computadora, sistema operativo y python son de 64 bits , entonces puede usar el módulo mmap para asignar el contenido del archivo a la memoria y acceder a él con índices y segmentos. Aquí un ejemplo de la documentación:
import mmap
with open("hello.txt", "r+") as f:
# memory-map the file, size 0 means whole file
map = mmap.mmap(f.fileno(), 0)
# read content via standard file methods
print map.readline() # prints "Hello Python!"
# read content via slice notation
print map[:5] # prints "Hello"
# update content using slice notation;
# note that new content must have same size
map[6:] = " world!/n"
# ... and read again using standard file methods
map.seek(0)
print map.readline() # prints "Hello world!"
# close the map
map.close()
Si su computadora, sistema operativo o python son de 32 bits , entonces los archivos grandes de tamaño variable pueden reservar una gran parte de su espacio de direcciones y starve su programa de memoria.
f = ... # file-like object, i.e. supporting read(size) function and
# returning empty string '''' when there is nothing to read
def chunked(file, chunk_size):
return iter(lambda: file.read(chunk_size), '''')
for data in chunked(f, 65536):
# process the data
ACTUALIZACIÓN: El enfoque se explica mejor en https://.com/a/4566523/38592