Faule Methode für große Datei in Python Lesen?
Frage
Ich habe eine sehr große Datei 4 GB und wenn ich versuche, es meinen Computer hängt zu lesen. Deshalb mag ich es Stück für Stück lesen und nach der Verarbeitung jedes Stück speichert das verarbeitete Stück in einer anderen Datei und nächstes Stück lesen.
Gibt es eine Methode, um diese Stücke zu yield
?
Ich würde gerne eine faul Methode haben .
Lösung
eine faule Funktion zu schreiben, benutzen Sie einfach 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)
Eine weitere Option wäre verwenden iter
und eine Hilfsfunktion:
f = open('really_big_file.dat')
def read1k():
return f.read(1024)
for piece in iter(read1k, ''):
process_data(piece)
Wenn die Datei zeilenbasiert, das Dateiobjekt ist bereits ein fauler Generator von Zeilen:
for line in open('really_big_file.dat'):
process_data(line)
Andere Tipps
Wenn der Computer, Betriebssystem und Python 64-Bit ist, dann können Sie die mmap Modul den Inhalt der Datei in den Speicher und greifen sie mit Indizes und Scheiben abzubilden. Hier ein Beispiel aus der Dokumentation:
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()
Wenn entweder Ihren Computer, Betriebssystem oder Python sind 32-Bit , dann mmap-ing große Dateien können große Teile des Adressraums und verhungern Ihr Programm Speicher.
file.readlines () nimmt in einem optionalen Größe Argumente, das die Anzahl der Zeilen in den Zeilen lesen annähert zurückgegeben.
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)
Es gibt bereits viele gute Antworten, aber ich lief in ein ähnliches Problem vor kurzem, und die Lösung, die ich benötigt ist hier nicht aufgeführt, so dass ich dachte ich diesen Thread ergänzen könnte.
80% der Zeit, ich brauche Dateien Zeile für Zeile zu lesen. Dann href="https://stackoverflow.com/a/519653/628786"> wie in diesem , Sie wollen Objekt die Datei verwenden, selbst als faul Generator:
with open('big.csv') as f:
for line in f:
process(line)
Allerdings habe ich vor kurzem lief in eine sehr, sehr groß (fast) einzige Zeile csv, wo die Zeilentrenn war in der Tat nicht '\n'
aber '|'
.
- Beim Lesen Zeile für Zeile war keine Option, aber ich brauche noch für Zeile es Zeile zu verarbeiten.
- Converting
'|'
'\n'
bevor auch indiskutabel war Verarbeitung, weil einige der Felder dieser csv enthalten'\n'
(freier Text Benutzereingabe). - die csv-Bibliothek verwenden wurde auch, weil die Tatsache, dass, zumindest in frühen Versionen der lib ausgeschlossen, es fest einprogrammiert wird, um die Eingabezeile für Zeile zu lesen.
Ich kam mit dem folgenden Ausschnitt oben:
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
ich es auf große Dateien und mit unterschiedlichen Blockgrößen erfolgreich getestet habe (ich habe sogar versucht, eine chunksize von 1 Byte, nur um sicherzustellen, dass der Algorithmus ist nicht größenabhängig).
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
UPDATE: Der Ansatz ist am besten erklärt in https://stackoverflow.com/a/4566523/38592
ich darf nicht aufgrund meiner geringen Ruf kommentieren, aber SilentGhosts Lösung sollte viel einfacher, mit file.readlines ([sizeHint])
seinedit: SilentGhost ist richtig, aber dies sollte besser sein als:
s = ""
for i in xrange(100):
s += file.next()
Ich denke, wir können so schreiben:
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)
Ich bin in einer ähnlichen Situation. Es ist nicht klar, ob Sie Chunkgröße in Bytes kennen; Normalerweise mache ich nicht, aber die Anzahl der Datensätze (Zeilen), die erforderlich ist, ist bekannt:
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))]
Aktualisieren : Danke nosklo. Hier ist, was ich meinte. Es funktioniert fast, mit der Ausnahme, dass es eine Linie ‚zwischen‘ Chunks verliert.
chunk = [next(gen) for i in range(lines_required)]
funktioniert der Trick w / o alle Zeilen zu verlieren, aber es sieht nicht sehr schön.
Siehe offizielle Dokumentation https: // docs .python.org / zh-cn / 3 / library / functions.html? #iter
Vielleicht ist diese Methode pythonic:
from functools import partial
"""A file object returned by open() is a iterator with
read method which could specify current read's block size"""
with open('mydata.db', 'r') as f_in:
part_read = partial(f_in.read, 1024*1024)
iterator = iter(part_read, b'')
for index, block in enumerate(iterator, start=1):
block = process_block(block) # process block data
with open(f'{index}.txt', 'w') as f_out:
f_out.write(block)
Um die Prozess Zeile für Zeile, ist dies eine elegante Lösung:
def stream_lines(file_name):
file = open(file_name)
while True:
line = file.readline()
if not line:
file.close()
break
yield line
Solange es keine Leerzeilen sind.
können Sie folgenden Code verwenden.
file_obj = open('big_file')
open () gibt ein Dateiobjekt
dann verwenden os.stat für immer Größe
file_size = os.stat('big_file').st_size
for i in range( file_size/1024):
print file_obj.read(1024)