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ą .

Author: Pratik Deoghare, 2009-02-06

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)
 338
Author: nosklo,
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.

 30
Author: Community,
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)
 25
Author: Anshul,
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"

 21
Author: Paolo Tedesco,
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).

 14
Author: user48678,
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

 8
Author: myroslav,
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)
 2
Author: TonyCoolZhu,
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])

Python file methods

Edit: SilentGhost ma rację, ale to powinno być lepsze niż:

s = "" 
for i in xrange(100): 
   s += file.next()
 1
Author: sinzi,
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.

 1
Author: SilentGhost,
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.
 0
Author: crizCraig,
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)
 -1
Author: shrikant,
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