Ленивый метод для чтения большого файла в Python?
У меня очень большой файл 4GB, и когда я пытаюсь его прочитать, мой компьютер зависает.
Поэтому я хочу прочитать его по частям и после обработки каждого куска сохранить обработанный кусок в другой файл и прочитать следующий кусок.
есть ли способ, чтобы yield эти куски ?
Я хотел бы, чтобы ленивый способ.
11 ответов:
чтобы написать ленивую функцию, просто используйте
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)
другой вариант - использовать
iterи вспомогательная функция:f = open('really_big_file.dat') def read1k(): return f.read(1024) for piece in iter(read1k, ''): process_data(piece)
если файл основан на строке, объект file уже является ленивым генератором строк:
for line in open('really_big_file.dat'): process_data(line)
Если ваш компьютер, ОС и python 64-разрядные, вы можете использовать модуль mmap для отображения содержимого файла в память и доступа к нему с индексами и срезами. Вот пример из документации:
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()Если ваш компьютер, ОС или python являются 32-разрядными, то mmap-ing большие файлы могут зарезервировать большую часть вашего адресного пространства и подохнут программы памяти.
.readlines () принимает необязательный аргумент size, который аппроксимирует количество строк, прочитанных в возвращенных строках.
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)
посмотри этот пост на Neopythonic: "сортировка миллиона 32-разрядных целых чисел в 2 МБ оперативной памяти с помощью Python"
уже есть много хороших ответов, но недавно я столкнулся с подобной проблемой, и решение, которое мне нужно, не указано здесь, поэтому я решил, что могу дополнить эту тему.
80% времени, мне нужно читать файлы построчно. Тогда, как и предполагалось в этом ответ, вы хотите использовать сам объект file в качестве ленивого генератора:
with open('big.csv') as f: for line in f: process(line)однако недавно я столкнулся с очень большой (почти) однострочной csv, где разделитель строк на самом деле не был
'\n'но'|'.
- чтение строка за строкой не было вариантом, но мне все еще нужно было обрабатывать его строка за строкой.
- преобразование
'|'до'\n'до обработки также не могло быть и речи, потому что некоторые поля этого csv содержали'\n'(свободный ввод текста пользователем).- использование библиотеки csv также было исключено из-за того, что, по крайней мере, в ранних версиях lib,он жестко закодирован для чтения входной строки линия.
Я придумал следующий код:
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я успешно протестировал его на больших файлах и с разными размерами кусков (я даже попробовал размер куска 1 байт, просто чтобы убедиться, что алгоритм не зависит от размера).
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обновление: подход лучше всего объяснить в https://stackoverflow.com/a/4566523/38592
Я думаю, что мы можем написать так:
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)
Я не могу комментировать из-за моей низкой репутации, но решение SilentGhosts должно быть намного проще с файлом.readlines([sizehint])
edit: SilentGhost прав, но это должно быть лучше, чем:
s = "" for i in xrange(100): s += file.next()
Я нахожусь в несколько похожей ситуации. Неясно, знаете ли Вы размер куска в байтах; я обычно этого не делаю, но количество записей (строк), которое требуется, известно:
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))]обновление: спасибо nosklo. Вот что я имел в виду. Это почти работает, за исключением того, что он теряет линию "между" кусками.
chunk = [next(gen) for i in range(lines_required)]делает трюк без потери каких-либо линий, но это не выглядит очень хорошим.
для обработки построчно, это элегантное решение:
def stream_lines(file_name): file = open(file_name) while True: line = file.readline() if not line: file.close() break yield lineпока нет пустых строк.
вы можете использовать следующий код.
file_obj = open('big_file')open () возвращает объект file
затем использовать ОС.стат для получения размера
file_size = os.stat('big_file').st_size for i in range( file_size/1024): print file_obj.read(1024)
Comments