ファイルをgzipで圧縮した後、特に圧縮されていないファイルサイズが4 GBを超える場合は、解凍せずに圧縮されていないファイルのサイズをすばやく照会する方法はありますか?
RFCによるとhttps://www.rfc-editor.org/rfc/rfc1952#page-5ファイルの最後の4バイトを照会できますが、圧縮されていないファイルが4 GBを超える場合、値はuncompressed value modulo 2^32
を実行して値を取得することもできますが、gunzip -l foo.gz
「非圧縮」列がuncompressed value modulo 2^32
再び含まれます。おそらく、上記のようにフッターを読んでいるからです。
まず、解凍せずに圧縮されていないファイルのサイズを取得する方法があるかどうか疑問に思います。これは、gzip圧縮ファイルに50 GB以上のデータが含まれていて、次の方法を使用して解凍するのに時間がかかる場合に特に便利です。gzcat foo.gz | wc -c
編集する:man
OSXに付属のユーティリティページでは、4 GBの制限を公に認めます()。gzip
Apple gzip 242
BUGS
According to RFC 1952, the recorded file size is stored in a 32-bit
integer, therefore, it can not represent files larger than 4GB. This
limitation also applies to -l option of gzip utility.
答え1
gzip
最速の方法は、冗長モードでテストが自分のシステムで解凍されたバイト数を7761108684バイトのファイルに出力するように変更することです。
% time gzip -tv test.gz
test.gz: OK (7761108684 bytes)
gzip -tv test.gz 44.19s user 0.79s system 100% cpu 44.919 total
% time zcat test.gz| wc -c
7761108684
zcat test.gz 45.51s user 1.54s system 100% cpu 46.987 total
wc -c 0.09s user 1.46s system 3% cpu 46.987 total
gzip(1.6、Debianで利用可能)を修正するには、パッチは次のようになります。
--- a/gzip.c
+++ b/gzip.c
@@ -61,6 +61,7 @@
#include <stdbool.h>
#include <sys/stat.h>
#include <errno.h>
+#include <inttypes.h>
#include "closein.h"
#include "tailor.h"
@@ -694,7 +695,7 @@
if (verbose) {
if (test) {
- fprintf(stderr, " OK\n");
+ fprintf(stderr, " OK (%jd bytes)\n", (intmax_t) bytes_out);
} else if (!decompress) {
display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr);
@@ -901,7 +902,7 @@
/* Display statistics */
if(verbose) {
if (test) {
- fprintf(stderr, " OK");
+ fprintf(stderr, " OK (%jd bytes)", (intmax_t) bytes_out);
} else if (decompress) {
display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out,stderr);
} else {
同様の方法1.11 以降のバージョンに実装され、gzip
含まれる予定です。gzip -l
次に、データを解凍してサイズを決定します。
答え2
gzip形式は圧縮されていないサイズを4バイト(ファイルの最後の4バイト)にのみ保存するため、保存されている圧縮されていないサイズは実際にはモジュロ2**32(4GiB)サイズです。実際の圧縮されていないサイズが4GiBより小さい場合、この値は正確ですが、4GiBを超える圧縮されていないファイルの場合、正しい値を取得する唯一の方法はファイル全体を読み取ることです。しかし、推定できる(Pythonコードは次のようになります)!
圧縮されたサイズが圧縮されていないサイズより大きい場合、圧縮されていないサイズは4GiBより大きくなる可能性があり、新しいサイズが圧縮されたサイズより大きく4GiBより大きくなるまで左側に「1」ビットを追加して正しいサイズを推測しようとします。 。この推測は、次の2つの理由で間違っている可能性があります。
- 場合によっては、圧縮されたサイズが圧縮されていないサイズよりも大きい場合があります(たとえば、圧縮ファイルを圧縮したい場合など)。
- 非常に大きなファイルの場合、ビット「1」を左に数回移動して、数字「1」と元の32ビットの間に「穴」を作成します(たとえば、5回移動すると10000Xになります。ここで、Xは生32です)。 -少し)。返される値は、圧縮されていないファイルの最小予想サイズです。ファイル全体を読み取らないと、穴を正しく「埋める」ことができないからです。
以下は、圧縮されていないサイズを推定するPythonコードです。マイ広告申込情報:
import os
import struct
def estimate_gzip_uncompressed_size(filename):
compressed_size = os.stat(filename).st_size
with open(filename, mode="rb") as fobj:
fobj.seek(-4, 2)
uncompressed_size = struct.unpack("<I", fobj.read())[0]
if compressed_size > uncompressed_size:
i, value = 32, uncompressed_size
while value <= 2**32 and value < compressed_size:
value = (1 << i) ^ uncompressed_size
i += 1
uncompressed_size = value
return uncompressed_size
gzipで圧縮されたCSVファイルをPostgreSQLにインポートすることを報告するための進行状況バーを実装するのに問題があったため、rows pgimport
実際のサイズを推定するために上記の関数を作成しました(プログラムはファイル全体を読み取るために推定が正しくありません。かどうかがわかります。正しい「新しい」値で進行状況バーを更新します。
ノート次の理由で:を使用してgzip --list <filename>
圧縮されていないサイズを取得することは私にとってオプションではありません。
- バージョン2.12より前は、このコマンドはすぐに実行されましたが、圧縮されていないサイズが正しく報告されていました(最後の4バイトのみが読み取られました)。
- バージョン2.12では、ファイル全体を読み取ってこのバグを修正します(圧縮されていないサイズのみを印刷するために!)。ファイルが大きくて時間がかかるため、これはオプションではありません。 ~から2.12 リリースノート:
"gzip -l"は、4GiB以上のファイル長を誤って報告しません。以前は、「gzip -l」は、値が圧縮されていない長さモジュールとして2 ** 32の場合でも、gzipヘッダに格納されている32ビット値を出力しました。ここで、「gzip -l」はデータを解凍し、結果のバイトを計算することによって非圧縮長を計算します。時間がかかるかもしれませんが、精度の利点はパフォーマンスの欠点よりも大きいようです。
答え3
他の答えで述べたように、これは不可能です。私が考えることができる唯一のケースは、圧縮ファイル自体が4GiB / 1032 = 3.97MiBより小さい場合です。これは、圧縮されていないサイズがgzipフッターに格納されている32ビットの「サイズ」をオーバーフローしないようにするためです。 1032 はい最大圧縮比。
あるいは、複数のスレッドを使用して解凍速度を上げて計算することもできます。このために私は書いた。高速gzip。 PyPIで使用できますが、ソースからビルドすることもできます。
python3 -m pip install --user rapidgzip
または、次のベンチマークに使用する最新バージョンの場合:
git clone https://github.com/mxmlnkn/rapidgzip
cd rapidgzip && mkdir build && cd build
cmake .. && make rapidgzip
Ryzen 3900Xのベンチマーク結果(物理コア12個/仮想コア24個):
デコーダ | ライン | 実行時間/秒 | 帯域幅/(MB/秒) |
---|---|---|---|
高速gzip- 計算 | 4294967296 | 0.589 | 7292 |
高速gzip-c| トイレ -c |
4294967296 | 1.279 | 3358 |
アーカイブ | 4294967296 | 9.088 | 473話 |
豚豚 | 4294967296 | 13.230 | 325 |
アーカイブ | 4294967296 | 22.167 | 194 |
これを行うには、ファイルを非常に高速なSSDに常駐させるか、メモリにキャッシュする必要があります。ファイルがローテーションディスクにある場合、I / Oは最初にパフォーマンスのボトルネックを引き起こします。igzip
完全な並列化がなくても、すでにほとんどのHDDよりも高速です。
sudo apt install pigz isal
python3 -m pip install --user --upgrade rapidgzip
# Create a compressible random file
base64 /dev/urandom | head -c $(( 4 * 1024 * 1024 * 1024 )) > 4GiB-base64
gzip -c 4GiB-base64
fileSize=$( stat -L --format=%s 4GiB-base64 )
printf '\n| %7s | %8s | %10s | %18s |\n' Decoder Lines 'Runtime / s' 'Bandwidth / (MB/s)'
printf -- '|---------|----------|-------------|--------------------|\n'
countedBytes=$( src/tools/rapidgzip --count "4GiB-base64.gz" )
runtime=$( ( time src/tools/rapidgzip --count "4GiB-base64.gz" ) 2>&1 | sed -n -E 's|real[ \t]*0m||p' | sed 's|[ \ts]||' )
bandwidth=$( python3 -c "print( int( round( $fileSize / 1e6 / $runtime ) ) )" )
printf '| %7s | %8s | %11s | %18s |\n' "rapidgzip --count" "$countedBytes" "$runtime" "$bandwidth"
for tool in src/tools/rapidgzip igzip pigz gzip; do
countedBytes=$( $tool -d -c "4GiB-base64.gz" | wc -c )
runtime=$( ( time $tool -d -c "4GiB-base64.gz" | wc -c ) 2>&1 | sed -n -E 's|real[ \t]*0m||p' | sed 's|[ \ts]||' )
bandwidth=$( python3 -c "print( int( round( $fileSize / 1e6 / $runtime ) ) )" )
printf '| %7s | %8s | %11s | %18s |\n' "$tool" "$countedBytes" "$runtime" "$bandwidth"
done
答え4
何について
gzip -l file.gz|tail -n1|awk '{print $2}'
numfmt --to=iec $(gzip -l file.gz|tail -n1|awk '{print $2}')