.NET GZipStream
は、ファイル内の最初のレコードのすべてである平文の最初の548バイトをアンパックします。 7Zipはファイル全体を1.2GBの出力ファイルに抽出しますが、レコードセパレータを使用しないプレーンテキスト(約130万行分)であり、7Zipでファイルをテストすると1,441バイトと報告されます。
私はいくつかを調べて、このことを直接解凍する単一の圧縮ライブラリを見つけることができませんでした。
ファイル内で少しキャストした後、1,441バイトが通常はgzipファイルの最後の4バイトであるISIZE
の値であることがわかりました。これは圧縮データに追加された8バイトフッターレコードの一部ですチャンク。
あなたが持っているのは、一緒に連結された.gzファイルの大きなセットです。そして、それはお尻の完全な痛みですが、あなたがこれにアプローチする方法はいくつかあります。
最初に、圧縮ファイルのgzipヘッダーシグネチャバイトをスキャンします:0x1F
と0x8B
です。これらのファイルを見つけると、(通常は)各.gzファイルの開始点がストリーム内にあります。ファイルのオフセットのリストを作成し、ファイルの各チャンクを抽出して解凍することができます。
もう1つの選択肢は、入力ストリームから消費されたバイト数を報告するライブラリを使用することです。ほとんどすべてのデコンプレッサーは何らかのバッファリングを使用するので、入力ストリームは消費されたバイト数よりもはるかに多く移動するので、これを直接推測することは困難です。しかし、DotNetZip
ストリームは、実際に消費された入力バイトを提供します。これを使用して、次の開始位置を調べることができます。これにより、ファイルをストリームとして処理し、各ファイルを個別に抽出することができます。
いずれにしても、高速ではありません。ここで
はDotNetZip
ライブラリを使用して、第二の選択肢のための方法です:
public static IEnumerable<byte[]> UnpackCompositeFile(string filename)
{
using (var fstream = File.OpenRead(filename))
{
long offset = 0;
while (offset < fstream.Length)
{
fstream.Position = p;
byte[] bytes = null;
using (var ms = new MemoryStream())
using (var unpack = new Ionic.Zlib.GZipStream(fstream, Ionic.Zlib.CompressionMode.Decompress, true))
{
unpack.CopyTo(ms);
bytes = ms.ToArray();
// Total compressed bytes read, plus 10 for GZip header, plus 8 for GZip footer
offset += unpack.TotalIn + 18;
}
yield return bytes;
}
}
}
それは醜いと高速ではありません(ファイル全体を解凍するために私に約48秒かかった)が、動作するように表示されます。各byte[]
出力は、ストリーム内の単一の圧縮ファイルを表します。これらは、System.Text.Encoding.UTF8.GetString(...)
で文字列に変換し、次に解析して意味を抽出することができます。
ファイル内の最後の項目は、次のようになります
WARC/1.0
WARC-Type: metadata
WARC-Target-URI: https://zverek-shop.ru/dljasobak/ruletka_sobaki/ruletka-tros_standard_5_m_dlya_sobak_do_20_kg
WARC-Date: 2017-11-25T14:16:01Z
WARC-Record-ID: <urn:uuid:e19ef645-b057-4305-819f-7be2687c3f19>
WARC-Refers-To: <urn:uuid:df5de410-d4af-45ce-b545-c699e535765f>
Content-Type: application/json
Content-Length: 1075
{"Container":{"Filename":"CC-MAIN-20171117170336-20171117190336-00002.warc.gz","Compressed":true,"Offset":"904209205","Gzip-Metadata":{"Inflated-Length":"463","Footer-Length":"8","Inflated-CRC":"1610542914","Deflate-Length":"335","Header-Length":"10"}},"Envelope":{"Format":"WARC","WARC-Header-Length":"438","Actual-Content-Length":"21","WARC-Header-Metadata":{"WARC-Target-URI":"https://zverek-shop.ru/dljasobak/ruletka_sobaki/ruletka-tros_standard_5_m_dlya_sobak_do_20_kg","WARC-Warcinfo-ID":"<urn:uuid:283e4862-166e-424c-b8fd-023bfb4f18f2>","WARC-Concurrent-To":"<urn:uuid:ca594c00-269b-4690-b514-f2bfc39c2d69>","WARC-Date":"2017-11-17T17:43:04Z","Content-Length":"21","WARC-Record-ID":"<urn:uuid:df5de410-d4af-45ce-b545-c699e535765f>","WARC-Type":"metadata","Content-Type":"application/warc-fields"},"Block-Digest":"sha1:4SKCIFKJX5QWLVICLR5Y2BYE6IBVMO3Z","Payload-Metadata":{"Actual-Content-Type":"application/metadata-fields","WARC-Metadata-Metadata":{"Metadata-Records":[{"Value":"1140","Name":"fetchTimeMs"}]},"Actual-Content-Length":"21","Trailing-Slop-Length":"0"}}}
これが後に2行の空白行を含む1441バイトを占めているレコードです。ただ、完全を期すために...
TotalIn
プロパティは、圧縮されたバイトの数は、gzipヘッダとフッタを含まない、読み取り
を返します。上記のコードでは、ヘッダーとフッターのサイズに一定の18バイトを使用します。これはGZipの最小サイズです。これはこのファイルでは機能しますが、連結されたGZipファイルを扱う人なら誰でもヘッダに大きなデータがあることが分かります。
- 直接gzipヘッダを解析し、解凍する
DeflateStream
を使用:あなたは2つのオプションがあります。この場合 。
TotalIn + 18
バイトから始まるGZipシグネチャバイトをスキャンします。
どちらも遅くなくても機能します。圧縮解除コードでバッファリングが行われているので、各セグメントの後ろでストリームを後ろ向きに探索しなければならないので、追加のバイトを読み込んでも遅くなることはありません。
これは小さいファイルでも動作することを確認しましたか?言って、メガバイトか?元の圧縮されていないファイルがUTF-8でエンコードされていることは確かですか? 'outmemstream'の内容をファイルに書き込んで、そこにあるものを確認しましたか?あなたは記憶がなくなっていないと確信していますか?そのコードは、2ギガバイト以上のRAM(memstreamの場合は280MB、おそらく圧縮されていないデータの場合は2倍、作成している文字列の場合は2倍)を食べるようです。 –
@JimMischel実際、それはそれより悪いです。圧縮されたデータはASCIIテキストのようです。圧縮されていないデータの場合は1.2GB、文字列表現の場合は2倍です。 – Corey