2011-02-08 46 views
12

我正在編寫一個腳本,它將與來自儀器的數據一起作爲gzip流使用。在大約90%的情況下,gzip模塊可以很好地工作,但其中一些流會導致產生IOError: Not a gzipped file。如果gzip標題被刪除,deflate流直接送到zlib,我改爲Error -3 while decompressing data: incorrect header check。在將我的頭撞向牆壁大約半天之後,我發現有問題的流包含看似隨機數量的附加字節(不是gzip數據的一部分),並附加到最​​後。我如何使用包含額外數據的Gzip文件?

這令我奇怪的是,Python不能使用這些文件的原因有兩個:

  1. GZIP和7zip的能夠打開這些「填充」的文件沒有問題。 (Gzip已產生的消息decompression OK, trailing garbage ignored,7zip的成功默默)
  2. 無論是gzip和Python文檔似乎表明,這應該工作:(重點煤礦)

    Gzip's format.txt

    必須能夠到 無論壓縮數據的實際大小如何,都可以使用任何壓縮方法檢測壓縮數據的結尾,即 。 特別地, 解壓縮器必須能夠面向記錄的文件系統上的檢測和跳過所附 額外的數據爲有效的壓縮文件,或者當 的壓縮數據只能從一個裝置在一個 的倍數讀一定的塊大小。

    Python's gzip.GzipFile`

    調用GzipFile對象的close()方法不會關閉FileObj文件,因爲你可能希望壓縮數據後追加更多的材料。這也可以讓您將打開寫作StringIO對象FileObj文件和檢索使用StringIO對象的getvalue()方法所產生的內存緩衝區。

    Python's zlib.Decompress.unused_data

    包含過去壓縮的數據的末尾的字節的任何一個字符串。也就是說,這將保持"",直到包含壓縮數據的最後一個字節可用。 如果整個串變成了包含壓縮數據,這是"",空字符串。

    的唯一方式來確定壓縮數據的字符串結尾是通過實際解壓縮它。這意味着,當壓縮數據包含在一個較大的文件的一部分,你只能通過讀取數據發現它的結束和餵養它其次是一些非空字符串爲減壓對象的decompress()方法,直到unused_data屬性不再空的字符串。

這裏是我試過的四種方法。 (這些例子是Python 3.1,但我測試過2.5和2.7,並且有同樣的問題。)

# approach 1 - gzip.open 
with gzip.open(filename) as datafile: 
    data = datafile.read() 

# approach 2 - gzip.GzipFile 
with open(filename, "rb") as gzipfile: 
    with gzip.GzipFile(fileobj=gzipfile) as datafile: 
     data = datafile.read() 

# approach 3 - zlib.decompress 
with open(filename, "rb") as gzipfile: 
    data = zlib.decompress(gzipfile.read()[10:]) 

# approach 4 - zlib.decompressobj 
with open(filename, "rb") as gzipfile: 
    decompressor = zlib.decompressobj() 
    data = decompressor.decompress(gzipfile.read()[10:]) 

我做錯了什麼?

UPDATE

好,同時與gzip的問題似乎是在模塊中的一個錯誤,我zlib問題是自己造成的。 ;-)

在挖掘到gzip.py時我意識到我在做什麼錯了 - 默認情況下,zlib.decompress等。期望zlib-wrapped流,而不是裸露的縮小流。通過傳入負值wbits,您可以告知zlib跳過zlib標題並對原始流進行解壓縮。這兩個工作:

# approach 5 - zlib.decompress with negative wbits 
with open(filename, "rb") as gzipfile: 
    data = zlib.decompress(gzipfile.read()[10:], -zlib.MAX_WBITS) 

# approach 6 - zlib.decompressobj with negative wbits 
with open(filename, "rb") as gzipfile: 
    decompressor = zlib.decompressobj(-zlib.MAX_WBITS) 
    data = decompressor.decompress(gzipfile.read()[10:]) 

回答

18

這是一個錯誤。 Python中gzip模塊的質量遠遠低於Python標準庫中應該要求的質量。

的這裏的問題是,gzip的模塊假定該文件是用gzip格式文件的流。在壓縮數據的末尾,它從頭開始,期待新的gzip頭文件;如果它找不到,就會引發異常。這是錯誤的。

當然,有效連接兩個gzip格式,如:

echo testing > test.txt 
gzip test.txt 
cat test.txt.gz test.txt.gz > test2.txt.gz 
zcat test2.txt.gz 
# testing 
# testing 

gzip數據模塊的錯誤是,如果沒有gzip的頁眉第二次應該不會引發異常;它應該只是結束文件。它應該只有如果第一次沒有標題會引發異常。

有沒有直接修改的gzip模塊沒有乾淨的解決方法;如果您想這樣做,請查看_read方法的底部。它應該設置另一個標誌,例如。 reading_second_block,告訴_read_gzip_header提高EOFError,而不是IOError

有此模塊中的其他漏洞。例如,它會不必要地尋找,導致其在非網絡流(例如網絡套接字)上失敗。這使我對這個模塊沒有多少信心:一個不知道gzip需要在沒有尋求的情況下才能正常工作的開發人員很難將其用於Python標準庫。

+0

其實我搞亂大約有`gzip`的內部位在調試的問題,但它沒有發生,我來解決那裏的問題並用我的腳本打包修改後的模塊。這真是醜陋,但聽起來它可能仍然是最好的選擇。 : -/ – 2011-02-08 01:29:56

+0

@Ben:它是獨立的,至少它不是一項重大成本;只有一個文件。本地模塊更令人討厭。 – 2011-02-08 01:34:11

4

我在過去類似的問題。我寫了一個new module,與流更好地協作。你可以嘗試一下,看看它是否適合你。

-1

我不能使它與上述技術工作。所以做了一個變通使用的壓縮文件包

import zipfile 
from io import BytesIO 
mock_file = BytesIO(data) #data is the compressed string 
z = zipfile.ZipFile(file = mock_file) 
neat_data = z.read(z.namelist()[0]) 

運行完美

相關問題