Как да определите Content-Length на gzipped файл в 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
възможен дубликат на Вземете некомпресиран размер на .gz файл в python   -  person Christian Berendt    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 GB и в) няма излишни боклуци в края на 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-файл

За съжаление, това работи само за файлове с размер ‹4gb поради използването само на 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 GB. - person Mark Adler; 21.06.2014
comment
Освен факта, че не всеки вид файл е seek-способен, какви биха били те? - person Dan Lenski; 21.06.2014
comment
Вижте моя отговор, включително връзка към друг отговор. - person Mark Adler; 21.06.2014
comment
Проблемът е, че работя върху архив от около 10 GB, така че вашата техника няма да работи тук. Благодаря ти все пак! - person Jprog; 23.06.2014

За да омекотя, трябва да отворя огромни компресирани файлове (> 4 GB), така че техниката на Дан няма да работи и искам дължината (броя на редовете) на файла, така че техниката на Марк Адлер не е подходяща.

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

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