Leniwa metoda czytania dużych plików w Pythonie?
Mam bardzo duży plik 4GB i kiedy próbuję go odczytać mój komputer się zawiesza. Chcę więc czytać kawałek po kawałku i po przetworzeniu każdego kawałka przechowywać przetworzony kawałek w innym pliku i czytać następny kawałek.
Czy jest jakaś metoda na yield
te kawałki ?
Chciałabym mieć metodę leniwą .
11 answers
Aby napisać leniwą funkcję, wystarczy użyć 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)
Inną opcją byłoby użycie iter
Oraz funkcja pomocnicza:
f = open('really_big_file.dat')
def read1k():
return f.read(1024)
for piece in iter(read1k, ''):
process_data(piece)
Jeśli plik jest oparty na linii, obiekt file jest już leniwym generatorem linii:
for line in open('really_big_file.dat'):
process_data(line)
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-02-06 09:30:56
Jeśli Twój komputer, system operacyjny i python są 64-bitowe, możesz użyć modułu mmap, aby zmapować zawartość pliku do pamięci i uzyskać do niego dostęp za pomocą indeksów i plasterków. Oto przykład z dokumentacji:
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()
Jeśli Twój komputer, system operacyjny lub python są 32-bitowe, wtedy duże pliki mmap mogą zarezerwować duże części przestrzeni adresowej i zagłodzić Twój program pamięci.
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-05-23 11:55:04
Plik.readlines() przyjmuje opcjonalny argument size, który przybliża liczbę wierszy odczytanych w zwracanych wierszach.
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)
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
2010-01-21 18:27:59
[[0]} spójrz na Ten post na Neopythonic : "sortowanie miliona 32-bitowych liczb całkowitych w 2MB pamięci RAM za pomocą Pythona"
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-02-06 09:28:12
Jest już wiele dobrych odpowiedzi, ale ostatnio natknąłem się na podobny problem i rozwiązanie, którego potrzebowałem, nie jest tutaj wymienione, więc pomyślałem, że mogę uzupełnić ten wątek.
80% czasu, muszę czytać pliki linia po linii. Następnie, zgodnie z sugestią w odpowiedzi , chcesz użyć samego obiektu file jako leniwego generatora:with open('big.csv') as f:
for line in f:
process(line)
Jednak ostatnio natknąłem się na bardzo duży (prawie) pojedynczy wiersz csv, gdzie separator wierszy w rzeczywistości nie był '\n'
, ale '|'
.
- Czytanie linii po linii nie było opcją, ale wciąż musiałem to przetworzyć wiersz po wierszu.
- Konwersja
'|'
do'\n'
przed przetworzeniem również nie wchodzi w grę, ponieważ niektóre z pól tego pliku csv zawierały'\n'
(dowolne wprowadzanie tekstu przez użytkownika). - Użycie biblioteki csv zostało również wykluczone, ponieważ, przynajmniej we wczesnych wersjach lib, jest zakodowane na twardo, aby odczytać linię wejściową po linii.
Wymyśliłem następujące fragment:
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
Przetestowałem go z powodzeniem na dużych plikach i o różnych rozmiarach fragmentów(próbowałem nawet rozmiaru 1 bajtu, aby upewnić się, że algorytm nie jest zależny od rozmiaru).
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-05-23 12:26:07
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
Aktualizacja: podejście najlepiej wyjaśnić w https://stackoverflow.com/a/4566523/38592
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-05-23 11:47:23
Myślę, że możemy napisać tak:
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)
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
2013-11-06 02:15:10
Nie mogę komentować ze względu na moją niską reputację, ale rozwiązanie SilentGhosts powinno być znacznie łatwiejsze z plikiem.readlines ([sizehint])
Edit: SilentGhost ma rację, ale to powinno być lepsze niż:
s = ""
for i in xrange(100):
s += file.next()
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-02-06 10:59:24
Jestem w podobnej sytuacji. Nie jest jasne, czy znasz rozmiar kawałka w bajtach; zwykle nie, ale liczba wymaganych rekordów (linii) jest znana:
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))]
Aktualizacja : dzięki nosklo. Oto, co miałem na myśli. To prawie działa, z tym, że traci linię "między" kawałki.
chunk = [next(gen) for i in range(lines_required)]
Robi sztuczkę bez utraty linii, ale nie wygląda to zbyt ładnie.
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-15 05:33:14
Aby przetwarzać linię po linii, jest to eleganckie rozwiązanie:
def stream_lines(file_name):
file = open(file_name)
while True:
line = file.readline()
if not line:
file.close()
break
yield line
Dopóki nie ma pustych linii.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
2012-05-01 23:12:15
Możesz użyć następującego kodu.
file_obj = open('big_file')
Open () zwraca obiekt file
Następnie użyj systemu operacyjnego.stat dla uzyskania rozmiaru
file_size = os.stat('big_file').st_size
for i in range( file_size/1024):
print file_obj.read(1024)
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-06-18 13:20:52