Ya hay muchas buenas respuestas, pero si su archivo completo está en una sola línea y aún desea procesar "filas" (en lugar de bloques de tamaño fijo), estas respuestas no lo ayudarán.
El 99% del tiempo, es posible procesar archivos línea por línea. Luego, como se sugiere en esta respuesta , puede usar el objeto de archivo en sí mismo como generador diferido:
with open('big.csv') as f:
for line in f:
process(line)
Sin embargo, una vez me encontré con un archivo muy, muy grande (casi) de una sola línea, donde el separador de filas no era '\n'
sino '|'
.
- Leer línea por línea no era una opción, pero aún necesitaba procesarlo fila por fila.
- La conversión
'|'
a '\n'
antes del procesamiento también estaba fuera de discusión, porque algunos de los campos de este csv contenían '\n'
(entrada del 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 lib, está codificado para leer la entrada línea por línea .
Para este tipo de situaciones, creé 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)
"""
curr_row = ''
while True:
chunk = f.read(chunksize)
if chunk == '': # End of file
yield curr_row
break
while True:
i = chunk.find(sep)
if i == -1:
break
yield curr_row + chunk[:i]
curr_row = ''
chunk = chunk[i+1:]
curr_row += chunk
Pude usarlo con éxito para resolver mi problema. Ha sido ampliamente probado, con varios tamaños de trozos.
Test suite, para aquellos que quieran convencerse a sí mismos.
test_file = 'test_file'
def cleanup(func):
def wrapper(*args, **kwargs):
func(*args, **kwargs)
os.unlink(test_file)
return wrapper
@cleanup
def test_empty(chunksize=1024):
with open(test_file, 'w') as f:
f.write('')
with open(test_file) as f:
assert len(list(rows(f, chunksize=chunksize))) == 1
@cleanup
def test_1_char_2_rows(chunksize=1024):
with open(test_file, 'w') as f:
f.write('|')
with open(test_file) as f:
assert len(list(rows(f, chunksize=chunksize))) == 2
@cleanup
def test_1_char(chunksize=1024):
with open(test_file, 'w') as f:
f.write('a')
with open(test_file) as f:
assert len(list(rows(f, chunksize=chunksize))) == 1
@cleanup
def test_1025_chars_1_row(chunksize=1024):
with open(test_file, 'w') as f:
for i in range(1025):
f.write('a')
with open(test_file) as f:
assert len(list(rows(f, chunksize=chunksize))) == 1
@cleanup
def test_1024_chars_2_rows(chunksize=1024):
with open(test_file, 'w') as f:
for i in range(1023):
f.write('a')
f.write('|')
with open(test_file) as f:
assert len(list(rows(f, chunksize=chunksize))) == 2
@cleanup
def test_1025_chars_1026_rows(chunksize=1024):
with open(test_file, 'w') as f:
for i in range(1025):
f.write('|')
with open(test_file) as f:
assert len(list(rows(f, chunksize=chunksize))) == 1026
@cleanup
def test_2048_chars_2_rows(chunksize=1024):
with open(test_file, 'w') as f:
for i in range(1022):
f.write('a')
f.write('|')
f.write('a')
# -- end of 1st chunk --
for i in range(1024):
f.write('a')
# -- end of 2nd chunk
with open(test_file) as f:
assert len(list(rows(f, chunksize=chunksize))) == 2
@cleanup
def test_2049_chars_2_rows(chunksize=1024):
with open(test_file, 'w') as f:
for i in range(1022):
f.write('a')
f.write('|')
f.write('a')
# -- end of 1st chunk --
for i in range(1024):
f.write('a')
# -- end of 2nd chunk
f.write('a')
with open(test_file) as f:
assert len(list(rows(f, chunksize=chunksize))) == 2
if __name__ == '__main__':
for chunksize in [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]:
test_empty(chunksize)
test_1_char_2_rows(chunksize)
test_1_char(chunksize)
test_1025_chars_1_row(chunksize)
test_1024_chars_2_rows(chunksize)
test_1025_chars_1026_rows(chunksize)
test_2048_chars_2_rows(chunksize)
test_2049_chars_2_rows(chunksize)
f = open('really_big_file.dat')
es solo un puntero sin consumo de memoria? (Quiero decir que la memoria consumida es la misma independientemente del tamaño del archivo). ¿Cómo afectará el rendimiento si uso urllib.readline () en lugar de f.readline ()?