Как определить длину содержимого сжатого файла в Python?

У меня есть большой сжатый файл, и я хочу узнать размер содержимого, не распаковывая его. Я пробовал это:

import gzip
import os

with gzip.open(data_file) as f:
          f.seek(0, os.SEEK_END)
          size = f.tell()

но я получаю эту ошибку

ValueError: Seek from end not supported 

Как я могу это сделать?

Спасибо.


person Jprog    schedule 20.06.2014    source источник
comment
В руководстве сказано, что os.SEEK_END предназначен для lseek. И это, наверное, то, что вам нужно-   -  person llrs    schedule 20.06.2014
comment
@Llopis, согласно документам file для Python 2.x, константы os.SEEK_ действительно подходят для использования с file.seek. Кроме того, os.lseek здесь не работает, потому что файлоподобный объект gzip не имеет базового файлового дескриптора уровня POSIX.   -  person Dan Lenski    schedule 20.06.2014


Ответы (3)


В принципе невозможно окончательно определить размер несжатых данных в файле gzip без его распаковки. Вам не нужно иметь место для хранения несжатых данных — вы можете отказаться от них по ходу дела. Но надо все распаковать.

Если вы контролируете источник gzip-файла и можете гарантировать, что а) в gzip-файле нет составных элементов, б) длина несжатых данных составляет менее 4 ГБ и в) нет постороннего мусора в конце файл gzip, тогда и только тогда вы можете прочитать последние четыре байта файла gzip, чтобы получить целое число с прямым порядком байтов, имеющее длину несжатых данных.

Подробнее см. этот ответ. .

Вот код Python для чтения файла gzip и печати несжатой длины без необходимости хранить или сохранять несжатые данные. Он ограничивает использование памяти небольшими буферами. Для этого требуется Python 3.3 или выше:

#!/usr/local/bin/python3.4
import sys
import zlib
import warnings
f = open(sys.argv[1], "rb")
total = 0
buf = f.read(1024)
while True:             # loop through concatenated gzip streams
    z = zlib.decompressobj(15+16)
    while True:         # loop through one gzip stream
        while True:     # go through all output from one input buffer
            total += len(z.decompress(buf, 4096))
            buf = z.unconsumed_tail
            if buf == b"":
                break
        if z.eof:
            break       # end of a gzip stream found
        buf = f.read(1024)
        if buf == b"":
            warnings.warn("incomplete gzip stream")
            break
    buf = z.unused_data
    z = None
    if buf == b"":
        buf == f.read(1024)
        if buf == b"":
            break
print(total)
person Mark Adler    schedule 21.06.2014
comment
Спасибо за ваш ответ, но я не уверен, что делает ваш код: дает ли он количество элементов в файле данных или размер (в байтах) несжатого файла? И какая функция в вашем коде нуждается в python3.3? Потому что я работаю на машине, на которой у меня нет выбора версии python (вероятно, 2.7) - person Jprog; 23.06.2014
comment
Несжатый размер в байтах. Метод eof для decompressobj был добавлен в версии 3.3 и необходим для распознавания того, что вы достигли конца потока gzip. - person Mark Adler; 23.06.2014

К сожалению, модуль gzip Python 2.x не поддерживает какой-либо способ определение размера несжатого файла.

Однако gzip сохраняет размер несжатого файла в виде 32-битного целого числа без знака с прямым порядком байтов в самом конце файла: http://www.abeel.be/content/determine-uncompressed-size-gzip-file

К сожалению, это работает только для файлов размером ‹4 ГБ из-за использования только 32-битного целого числа в формате gzip; см. руководство.

import os
import struct

with open(data_file,"rb") as f:
    f.seek(-4, os.SEEK_END)
    size, = struct.unpack("<I", f.read(4))
    print size
person Dan Lenski    schedule 20.06.2014
comment
У этого подхода есть и другие проблемы, помимо ограничения в 4 ГБ. - person Mark Adler; 21.06.2014
comment
Кроме того факта, что не каждый тип файла может быть seek, что это может быть? - person Dan Lenski; 21.06.2014
comment
Смотрите мой ответ, включая ссылку на другой ответ. - person Mark Adler; 21.06.2014
comment
Проблема в том, что я работаю с архивом размером около 10 ГБ, поэтому ваша техника здесь не сработает. В любом случае, спасибо! - person Jprog; 23.06.2014

Для вывода мне нужно открыть огромные сжатые файлы (> 4 ГБ), поэтому техника Дэна не сработает, и мне нужна длина (количество строк) файла, поэтому техника Марка Адлера не подходит.

В конце концов, я нашел для несжатых файлов решение (не самое оптимизированное, но оно работает!), Которое можно легко перенести в сжатые файлы:

size = 0

with gzip.open(data_file) as f:
  for line in f:
    size+= 1
    pass

return size

Всем спасибо, люди на этом форуме очень действенные!

person Jprog    schedule 26.06.2014
comment
В вашем вопросе не указано количество строк. На самом деле ваш пример попытки, если бы он сработал, вернул бы количество байтов из f.tell(), а не количество строк. - person Mark Adler; 21.08.2015