2012-01-10 80 views
3

我有一個二進制字符串,它包含兩個gzip二進制文件連接起來。 (我讀級聯2個gzip的文件一起二進制文件日誌文件)Ruby解析gzip二進制字符串

換句話說,我的equivalient:

require 'zlib' 
require 'stringio' 

File.open('t1.gz', 'w') do |f| 
    gz = Zlib::GzipWriter.new(f) 
    gz.write 'part one' 
    gz.close 
end 

File.open('t2.gz', 'w') do |f| 
    gz = Zlib::GzipWriter.new(f) 
    gz.write 'part 2' 
    gz.close 
end 


contents1 = File.open('t1.gz', "rb") {|io| io.read } 
contents2 = File.open('t2.gz', "rb") {|io| io.read } 

c = contents1 + contents2 

gz = Zlib::GzipReader.new(StringIO.new(c)) 

gz.each do | l | 
    puts l 
end 

當我嘗試解壓縮合並字符串,我只得到第一串。我如何獲得這兩個字符串?

+0

首先,它將有助於獲得您正在使用的實際代碼,而不是一些近似值。其次,你如何解壓縮數據? – 2012-01-10 16:00:10

+0

@FrederickCheung他通過GzipReader解壓縮。而這個代碼可能是他的實際代碼,只是沒有不必要的和令人困惑的業務邏輯。 – WattsInABox 2014-04-16 14:12:27

回答

3
while c 
    io = StringIO.new(c) 
    gz = Zlib::GzipReader.new(io) 
    gz.each do | l | 
    puts l 
    end 
    c = gz.unused # take unprocessed portion of the string as the next archive 
end 

ruby-doc

0

gzip格式使用包含以前壓縮數據的校驗和的頁腳。一旦達到頁腳,就不能再有相同數據流的數據了。

看起來,Ruby Gzip閱讀器只是在第一個遇到的頁腳之後完成閱讀,這在技術上是正確的,但是如果還有更多的數據,許多其他實現會引發一個錯誤。我並不真正瞭解Ruby的確切行爲。

問題是,您可以只連接原始字節流,並期望事情能夠工作。您必須實際適應流並重寫頁眉和頁腳。有關詳細信息,請參閱this question

或者你也可以解壓縮流,將它們連接起來,並重新壓縮,但顯然產生了一些開銷...

+0

我沒有寫日誌文件。我只是想讀它。我想解壓縮已經連接的兩個gz。我想避免重新創建你所關聯問題的第三個gz。 – Tihom 2012-01-10 16:24:47

+0

@Tihom:根據http://en.wikipedia.org/wiki/Gzip,連接幾個GZIP文件是完全有效的:「雖然它的文件格式也允許連接多個這樣的流(壓縮文件簡單地解壓縮連接爲如果它們最初是一個文件)......「當然,這與在一個GZIP存檔中壓縮文件不同。 – 2012-01-10 16:58:52

0

接受的答案不適用於我。這是我的修改版本。注意gz.unused的不同用法。

另外,您應該在GzipReader實例上調用finish以避免內存泄漏。

# gzcat-test.rb 
require 'zlib' 
require 'stringio' 
require 'digest/sha1' 

# gzip -c /usr/share/dict/web2 /usr/share/dict/web2a > web-cat.gz 
io = File.open('web-cat.gz') 
# or, if you don't care about memory usage: 
# io = StringIO.new File.read 'web-cat.gz' 

# these will be hashes: {orig_name: 'filename', data_arr: unpacked_lines} 
entries=[] 
loop do 
    entries << {data_arr: []} 
    # create a reader starting at io's current position 
    gz = Zlib::GzipReader.new(io) 
    entries.last[:orig_name] = gz.orig_name 
    gz.each {|l| entries.last[:data_arr] << l } 
    unused = gz.unused # save this before calling #finish 
    gz.finish 

    if unused 
    # Unused is not the entire remainder, but only part of it. 
    # We need to back up since we've moved past the start of the next entry. 
    io.pos -= unused.size 
    else 
    break 
    end 
end 

io.close 

# verify the data 
entries.each do |entry_hash| 
    p entry_hash[:orig_name] 
    puts Digest::SHA1.hexdigest(entry_hash[:data_arr].join) 
end 

運行:

> ./gzcat-test.rb 
web2" 
a62edf8685920f7d5a95113020631cdebd18a185 
"web2a" 
b0870457df2b8cae06a88657a198d9b52f8e2b0a 

我們解開內容符合原稿:

> shasum /usr/share/dict/web* 
a62edf8685920f7d5a95113020631cdebd18a185 /usr/share/dict/web2 
b0870457df2b8cae06a88657a198d9b52f8e2b0a /usr/share/dict/web2a 
0

這是爲了確保整個文件的正確方法是閱讀。儘管未使用可能爲零並不意味着已經達到原始gzip文件的末尾。

File.open(path_to_file) do |file| 
    loop do 
    gz = Zlib::GzipReader.new file 
    puts gz.read 

    unused = gz.unused 
    gz.finish 

    adjust = unused.nil? ? 0 : unused.length 
    file.pos -= adjust 
    break if file.pos == file.size 
    end 
end