圧縮された巨大なプレーンテキストファイルを部分的に抽出する方法は?


19

1.5 GBのサイズのzipファイルがあります。

その内容は1つのばかげた大きなプレーンテキストファイル(60 GB)であり、現在、すべてを抽出するのに十分なディスク領域がディスクに残っていません。

私のユースケースに関しては、コンテンツの一部を検査できれば十分です。

したがって、ファイルをストリームとして解凍し、ファイルの範囲にアクセスします(通常のテキストファイルの先頭と末尾を経由してアクセスできるように)。

メモリ(例:32GBマークから始まる最大100kbを抽出)または行(プレーンテキスト行3700-3900を指定)のいずれかを使用します。

それを達成する方法はありますか?


1
残念ながら、zip内の個々のファイルを検索することはできません。。どのsoloutionはあなたに興味を持っているポイントにファイルをバックアップして読んで関与しますので
plugwash

5
@plugwash質問を理解したように、目標はzipファイル(または解凍されたファイル)を読み取ることを避けることではなく、単に解凍されたファイル全体をメモリまたはディスクに保存することを避けることです。基本的に、解凍されたファイルをストリームとして扱います
ShreevatsaR

回答:


28

ファイルgzipを抽出できることに注意してくださいzip(少なくともファイルの最初のエントリzip)。そのアーカイブに巨大なファイルが1つしかない場合は、次のことができます。

gunzip < file.zip | tail -n +3000 | head -n 20

たとえば、3000番目の行から始まる20行を抽出します。

または:

gunzip < file.zip | tail -c +3000 | head -c 20

同じことをバイトで(headサポートする実装を想定して)-c)。

アーカイブ内の任意のメンバーについて、Unixyの方法で:

bsdtar xOf file.zip file-to-extract | tail... | head...

headビルトインksh93(when /opt/ast/bininが先にあるなど$PATH)で、次のこともできます:

.... | head     -s 2999      -c 20
.... | head --skip=2999 --bytes=20

いずれの場合でも、gzip/ bsdtar/ unzipは常に、抽出したい部分につながるファイルのセクション全体を圧縮解除(およびここで破棄)する必要があることに注意してください。それは、圧縮アルゴリズムの仕組みにかかっています。


場合はgzip、それを扱うことができ、他の「Z対応」ユーティリティ(意志zcatzlessなど)も動作しますか?
ivanivan

@ivanivan、それらが基づいているシステムgzip(一般的にはzlesszcat一部のシステムでは必ずしも.Zファイルの読み取り専用であるとは限りません)、はい。
ステファンシャゼラス

14

unzip -pおよびddを使用する1つのソリューション。たとえば、1000ブロックのオフセットで10kbを抽出します。

$ unzip -p my.zip | dd ibs=1024 count=10 skip=1000 > /tmp/out

注:私は本当に巨大なデータでこれを試しませんでした...


単一のアーカイブ内に複数のファイルがある一般的なケースではunzip -l ARCHIVE、アーカイブのコンテンツをリストしunzip -p ARCHIVE PATH、単一のオブジェクトのコンテンツPATHを標準出力に抽出するために使用できます。
デビッドフォースター

3
通常、ddcountまたはskipでパイプを使用することはread()最大 1024バイトのsを実行するため、信頼できません。したがってunzip、サイズが1024の倍数のチャンクでパイプに書き込む場合にのみ、正常に動作することが保証されます。
StéphaneChazelas 18年

4

その大きなzipファイルの作成を制御できる場合はgzip、との組み合わせの使用を検討してください。zlessください。

これによりzless、ページャーとして使用して、抽出に煩わされることなくファイルの内容を表示できます。

圧縮形式を変更できない場合、これは明らかに機能しません。もしそうなら、私zlessはむしろ便利だと思う。


1
しません。外部会社から提供された圧縮ファイルをダウンロードしています。
k0pernikus

3

ファイルの特定の行を表示するには、出力をUnixストリームエディターsedにパイプします。これにより、任意の大きなデータストリームを処理できるため、データの変更にも使用できます。要求された3700〜3900行を表示するには、次を実行します。

unzip -p file.zip | sed -n 3700,3900p

7
sed -n 3700,3900pファイルの最後まで読み続けます。それsed '3700,$!d;3900q'を回避するために使用することをおtail -n +3700 | head -n 201
勧めし

3

ファイルの先頭からその時点までを解凍するよりも効率的なことは何ができるのだろうかと思いました。答えはノーだと思われます。ただし、一部のCPU(Skylake)zcat | tailでは、CPUを最大クロック速度まで上げません。下記参照。カスタムデコーダーは、この問題を回避し、パイプ書き込みシステムコールを節約でき、おそらく10%速くなります。(または、電源管理設定を微調整しない場合、Skylakeでは最大60%高速です)。


カスタマイズされたzlibでできること skipbytes関数をは、圧縮ブロック内のシンボルを解析して、実際に圧縮解除ブロックを再構築する作業を行わずに最後まで到達することです。これは、zlibの通常のデコード機能を呼び出して同じバッファーを上書きし、ファイル内を前方に移動するよりも大幅に高速(おそらく少なくとも2倍)になります。しかし、そのような関数を誰かが書いたかどうかはわかりません。(そして、特定のブロックでデコーダーが再起動できるようにファイルが特別に書かれていない限り、これは実際には機能しないと思います)。

それは次のようになりますので、私は、それらをデコードせずにデフレートのブロックをスキップする方法があった期待していたくらい速いです。ハフマンツリーは各ブロックの先頭に送信されるため、任意のブロックの先頭からデコードできます(と思います)。ああ、デコーダーの状態はハフマンツリーよりも大きいと思います。これは以前の32kiBのデコードされたデータでもあり、デフォルトではブロック境界を越えてリセット/忘れ​​られません。同じバイトが繰り返し参照され続ける可能性があるため、巨大な圧縮ファイルでは文字通り一度だけ表示される場合があります。(たとえば、ログファイルでは、ホスト名は圧縮辞書で常に「ホット」のままであり、そのすべてのインスタンスは最初のインスタンスではなく前のインスタンスを参照します)。

zlibマニュアルでは、あなたが使用する必要が言うZ_FULL_FLUSH呼び出すときにdeflateあなたが圧縮されたストリームは、その時点までシークできるようにする場合。それは「圧縮状態をリセットする」ので、それなしでは、後方参照は前のブロックに入ることができると思います。したがって、zipファイルがときどきフルフラッシュブロックで書き込まれない限り(すべての1Gまたは何かが圧縮にほとんど影響を与えないように)、私はあなたが当初よりもあなたが望むポイントまでより多くの作業を行わなければならないと思います考え。おそらく、どのブロックの先頭からも開始できないと思います。


これの残りは、あなたが望む最初のバイトを含むブロックの始まりを見つけて、そこからデコードすることだけが可能であると考えている間に書かれました。

しかし、残念なことに、Deflateブロックの開始は、圧縮ブロックの長さを示していません。非圧縮データは、先頭が16ビットサイズの非圧縮ブロックタイプでコーディングできますが、圧縮ブロックはそうではありません。RFC1951は、フォーマットを非常に読みやすく記述しています。動的なハフマンコーディングのブロックには、ブロックの前にツリーがあります(したがって、圧縮解除プログラムはストリーム内をシークする必要がありません)。

最大後方参照距離はわずか32kiBであるため、圧縮プログラムは非圧縮データを多くメモリに保持する必要はありませんが、ブロックサイズを制限することはありません。ブロックの長さは数メガバイトです。(これは、現在のブロックを解析せずに現在のブロックの終わりを見つけることができた場合、磁気ドライブでもディスクシークがメモリにシーケンシャルに読み込み、RAMのデータをスキップするのに十分な大きさです)。

zlibは可能な限り ブロックを作成します。MarcAdlerによると、zlibはシンボルバッファがいっぱいになったときにのみ新しいブロックを開始します。デフォルト設定では、16,383シンボル(リテラルまたは一致)です。


出力をgzipで圧縮しましたseq(これは非常に冗長であり、おそらく素晴らしいテストではありません)が、そのpv < /tmp/seq1G.gz | gzip -d | tail -c $((1024*1024*1000)) | wc -c上でDDR4-2666 RAMを搭載した3.9GHzのSkylake i7-6700kで〜62 MiB / sの圧縮データで実行されます。これは246MiB / sの圧縮解除されたデータです。これはmemcpy、キャッシュに収まるには大きすぎるブロックサイズの場合、〜12 GiB / sの速度と比較して急激に変化します。

(の代わりにenergy_performance_preferenceデフォルトに設定balance_powerするとbalance_performance、Skylakeの内部CPUガバナーは、2.7GHz、〜43 MiB / sの圧縮データでのみ実行することを決定します。これsudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;do echo balance_performance > "$i";done'を調整するために使用します。このような頻繁なシステムコールは、実際のCPUバインドのようには見えません電源管理ユニットに働きかけます。)

TL:DR:非常に遅いディスクzcat | tail -cがない限り、高速CPUでもCPUバウンドです。 gzipは、実行したCPUの100%を使用し(およびによれば、クロックあたり1.81命令を実行しperf)、tail実行したCPUの0.162を使用しました(0.58 IPC)。それ以外の場合、システムはほとんどアイドル状態でした。

Linux 4.14.11-1-ARCHを使用しています。これは、デフォルトでKPTIが有効になっおり、Meltdownを回避するため、これらのwriteシステムコールはすべて、gzip以前よりも高価です:/


unzipまたはにシークが組み込まれているzcat(ただし、通常のzlibデコード機能を使用している)と、これらのパイプ書き込みがすべて保存され、Skylake CPUが最高のクロック速度で実行されます。(特定の種類の負荷に対するこのダウンクロックは、OSからCPU周波数の意思決定をオフロードするIntel Skylake以降に固有のものです。CPUが実行していることに関するデータが多く、より速く増減できるためです。通常は良好ですが、ここでは、より保守的なガバナー設定では、Skylakeが最高速度まで上昇しないことにつながります)。

システムコールはなく、必要な開始バイト位置に達するまでL2キャッシュに収まるバッファを書き換えるだけで、少なくとも数%の違いが生じる可能性があります。たぶん10%でも、ここで数字を作っているだけです。zlibキャッシュフットプリントの大きさ、およびKPTIが有効になっているすべてのシステムコールでのTLBフラッシュ(およびuop-cacheフラッシュ)がどれだけ痛いかを確認するための詳細なプロファイルは作成していません。


シークインデックスをgzipファイル形式に追加するソフトウェアプロジェクトがいくつかあります。誰にもシーク可能な圧縮ファイルを生成してもらうことができない場合、これは役に立ちませんが、他の将来の読者が恩恵を受けるかもしれません。

インデックスはときにのみ動作するように設計しているので、おそらくどちらもこれらのプロジェクトの、インデックスなし圧縮ストリームをスキップする方法を知っているデコード機能を持たない利用できます。


1

pythonセッションでzipファイルを開くことができます。zf = zipfile.ZipFile(filename, 'r', allowZip64=True)一度開くと、zipアーカイブ内の任意のファイルを開いて、通常のファイルのようにそこから行を読み取ることができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.