Подтвердить что ты не робот

Более чистый способ читать /gunzip огромный файл в python

Итак, у меня есть довольно гигантские .gz файлы - мы говорим от 10 до 20 g каждый при распаковке.

Мне нужно пропустить каждую строку из них, поэтому я использую стандарт:

import gzip
f = gzip.open(path+myFile, 'r')
for line in f.readlines():
    #(yadda yadda)
f.close()

Однако обе команды open() и close() принимают AGES, используя 98% памяти + CPU. Настолько, что программа выходит и выводит Killed на терминал. Может быть, он загружает весь извлеченный файл в память?

Теперь я использую что-то вроде:

from subprocess import call
f = open(path+'myfile.txt', 'w')
call(['gunzip', '-c', path+myfile], stdout=f)
#do some looping through the file
f.close()
#then delete extracted file

Это работает. Но есть ли более чистый способ?

4b9b3361

Ответ 1

Я на 99% уверен, что ваша проблема не в gzip.open(), а в readlines().

Как в документации объясняется:

f.readlines() возвращает список, содержащий все строки данных в файле.

Очевидно, что это требует чтения и декомпрессии всего файла и создания абсолютно гигантского списка.

Скорее всего, на самом деле malloc вызывает выделение всей памяти, которая ведется навсегда. И затем, в конце этой области (предполагая, что вы используете CPython), она должна GC, что весь гигантский список, который также займет навсегда.

Вы почти никогда не хотите использовать readlines. Если вы не используете очень старый Python, просто выполните следующее:

for line in f:

A file - это итерируемый полный строк, как и list, возвращаемый readlines - если он не является фактически list, он генерирует больше строк "на лету", читая из буфера. Таким образом, в любой момент времени вы будете иметь только одну строку и пару буферов порядка 10 МБ, а не 25 ГБ list. И чтение и распаковка будут распространяться по времени жизни цикла, а не делать все сразу.

Из быстрого теста, с файлом gzip на 3,5 ГБ, gzip.open() работает мгновенно, for line in f: pass занимает несколько секунд, gzip.close() эффективно мгновенно. Но если я делаю for line in f.readlines(): pass, это берет... ну, я не уверен, как долго, потому что через минуту моя система пошла на своп, прервав ад, и мне пришлось заставить убить переводчика, чтобы заставить его ответить на что-нибудь...


Так как это вызвало еще дюжину раз с момента этого ответа, я написал этот пост в блоге, который объясняет немного больше.

Ответ 2

Посмотрите pandas, в частности инструменты IO. Они поддерживают сжатие gzip при чтении файлов, и вы можете читать файлы в кусках. Кроме того, pandas работает очень быстро и эффективно.

Как я никогда не пробовал, я не знаю, насколько хорошо сжатие и чтение в кусках живут вместе, но, возможно, стоит попробовать попробовать