現在流行的linux發行版經常會提供一個新型的壓縮工具,叫做XZ Utils。可以去http://distrowatch.com上查看各個發行版本的配置信息。該工具可以得到更高的壓縮率。可以做的比其他常見壓縮算法要好。
關於 XZ Utils
XZ Utils 是爲 POSIX 平臺開發具有高壓縮率的工具。它使用 LZMA2 壓縮算法,生成的壓縮文件比 POSIX 平臺傳統使用的 gzip、bzip2 生成的壓縮文件更小,而且解壓縮速度也很快。最初 XZ Utils 的是基於 LZMA-SDK 開發,但是 LZMA-SDK 包含了一些 WINDOWS 平臺的特性,所以 XZ Utils 爲以適應 POSIX 平臺作了大幅的修改。XZ Utils 的出現也是爲了取代 POSIX 系統中舊的 LZMA Utils。
XZ Utils 主要包含了下列部分:
- 命令行程序 xz,用來生成和解壓縮 .xz 壓縮文件。
- 一組實用的腳本工具 (xzcat, xdiff, xzgrep 等)提供瀏覽,查找以及比較 .xz 文件內容等功能。
- liblzma 壓縮庫,提供算法的實現和近似於 ZLIB 的編程接口。
- 提供對 LZMA Utils 的一些兼容
xz 文件格式
XZ Utils 工具生成的壓縮文件擴展名爲 .xz (MIME 類型爲"application/x-xz")。.xz 文件格式具有下列特點:
基於數據流: 易於通過管道 (pipe) 生成壓縮文件或解壓縮文件。.xz 文件格式與 .gz/.bz2 文件一樣,不具備對多個文件進行歸檔打包的能力。若要處理多個文件,可以和歸檔工具 tar 結合使用,生成擴展名爲 .tar.xz 或 .txz 的壓縮文件。
隨機讀取: 存儲的數據被劃分爲獨立的壓縮塊,並對每個壓縮塊進行索引,當每個壓縮塊比較小時,便能夠進行有限的隨機讀取壓縮數據。
完整性驗證: 可以使用 CRC32、CRC64、SHA-256 來進行數據的完整性驗證,也可以增加自定義驗證方法。
可連接(concatenation): 類似於 .gz/.bz2 文件,可以把多個壓縮數據流連接到一個文件中。解壓縮時,就像解壓一個正常單壓縮流文件一樣。
支持多filter和filter鏈: 提供自定義 filter 的能力,也能夠將多個 filter 組成 filter 鏈,對數據進行處理。這點與 Unix 命令間使用的管道 (pipe) 類似。
可填充(padding): 可以在 .xz 文件末尾填充二進制'0'以充滿特定大小的空間,比如備份磁帶上的一個塊 (block)。
LZMA2 壓縮算法
LZMA2 是 .xz 文件格式使用的壓縮算算法。LZMA(Lempel-Ziv-Markov chain-Algorithm) 原本是 Windows 平臺上著名壓縮工具 7-Zip 中的 .7z 文件格式的默認壓縮算法。LZMA 使用類似於 LZ77 的字典編碼機制。在大多數情況下,LZMA 算法都能夠提供很高壓縮率和較快的壓縮速度。LZMA2 是 LZMA 的改進版本,相對於 LZMA,LZMA2 增進了 encoder 和 decoder 的實現,並改善了對多線程的支持。
高壓縮率
與 POSIX 系統上傳統的壓縮工具 gzip、bzip2 相比,XZ Utils 吸引人的地方在於,它能夠提供更高的壓縮率,生成的文件更小,而且解壓數據的速度也很快。XZ Utils 的主頁上描述:對於典型文件,XZ Utils 能夠生成比使用 gzip 小 30%,比使用 bzip 小 15% 的壓縮文件。
下面便針對壓縮率、壓縮速度和解壓速度三個方面進行簡單的驗證。
測試環境的 CPU 爲 AMD Athlon X4 3.0GHz,內存爲 8G。操作系統爲 64 位版本的 Gentoo Linux。所有的測試均在 /dev/shm(即內存設備)下進行,這樣儘可能避免了 I/O 讀寫對測試結果的影響。
測試內容爲 gzip、bzip2、xz 分別壓縮原始大小爲 445M 的 linux-kernel-3.2.1.tar 文件,然後解壓縮生成的壓縮文件。測試分兩組:
- gzip、bzip2 和 xz 分別使用參數"-1"(最小壓縮率)至"-9"(最大壓縮率)壓縮文件。
- 不設定壓縮率參數既使用各自的默認壓縮率。gzip 和 xz 默認壓縮率參數均爲"-6",bzip2 默認相應的爲"-9"。
分別記錄下生成的壓縮文件大小,壓縮用時,解壓用時數據。將所有收集到的數據進行整理。
處理後,使用 gnuplot 工具分別生成了下面的三個柱狀比較圖。下列柱狀圖中 X 軸的 "-" 表示使用默認壓縮率。
壓縮後文件大小的比較如下圖:
根據上圖,清楚地顯示 xz 生成的壓縮文件最小,即具有高壓縮率。且通過圖中比較可以看出,xz 生成的壓縮文件大小的確約比 gzip 生成的文件小 30%,比 bzip 生成的文件小 15%。
壓縮用時的比較如下圖:
由此圖可以看到 xz 的解壓速度和 gzip 一樣有着穩定的表現。大幅少於 bzip2 的解壓縮用時,雖比 gzip 用時稍多,但從 xz Utils 在同等條件下比 gzip 擁有更出色高壓縮率來看,解壓縮用時的表現也是不錯的。
從以上的驗證數據的對比可以看出:XZ Utils 具有 高壓縮率,解壓速度快的特點。能夠生成更小文件的同時,也能提供穩定快速的解壓,在對數據大小比較敏感的場合,比如說大數據的網絡傳輸,文件的備份,處理能力有限的嵌入系統等場合,有着十分廣泛的用途。
使用 xz 命令
xz 命令的基本用法
-
xz 命令幫助信息
$ xz --help Usage: xz [OPTION]... [FILE]... Compress or decompress FILEs in the .xz format. -z, --compress force compression -d, --decompress force decompression -t, --test test compressed file integrity -l, --list list information about .xz files -k, --keep keep (don't delete) input files -f, --force force overwrite of output file and (de)compress links -c, --stdout write to standard output and don't delete input files -0 ... -9 compression preset; default is 6; take compressor *and* decompressor memory usage into account before using 7-9! -e, --extreme try to improve compression ratio by using more CPU time; does not affect decompressor memory requirements ...
-
壓縮一個文件 test.txt,壓縮成功後生成 test.txt.xz, 原文件會被刪除。
$ xz test.txt $ ls test.txt* test.txt.xz
-
解壓 test.txt.xz 文件,並使用參數 -k 保持原文件不被刪除,
$ xz -d -k test.txt.xz $ ls test.txt* test.txt.xz test.txt
-
使用參數 -l 顯示 .xz 文件的基本信息。基本信息包括壓縮率、數據完整性驗證方式等。也可以和參數 -v 或 -vv 配合顯示更詳盡的信息。
$ xz -l index.txt.xz Strms Blocks Compressed Uncompressed Ratio Check Filename 1 1 768 B 1,240 B 0.619 CRC64 index.txt.xz
-
使用參數 -0, -1, -2, … -6, … -9 或參數 --fast, --best 設定壓縮率。xz 命令的默認爲 -6 ,對於大多數系統來說,甚至是一些較舊的系統,-4 … -6 壓縮率預設值都不錯的表現。
$ xz -k7 xz_pipe_decomp_mini.c $ xz -k --fast xz_pipe_decomp_mini.c
-
和 tar 命令配合處理多個文件。一般來說,有兩種簡單的方法將 tar 和 xz 結合起來,一是使用管道,或是使用 tar 命令的參數'J'。兩種方法各自有各自的優點,第一種方法可以充分利用 xz 利用豐富的參數。而第二種方法使用起來更簡單容易。例子分別如下
-
使用管道
$ tar cf - *.c | xz --best > src.tar.xz # compress $ xz -d src.tar.xz --stdout | tar -x # decompress
-
使用 tar 參數 "J
$ tar cJf src.txz *.c # compress $ tar xJf src.txz # decompress
-
使用管道
複雜的用法
-
使用參數 -H 顯示 xz 命令所有 options. 參數 -H 比使用參數 --help 顯示的內容更詳細。
$ xz -H | more
-
藉助 xargs 命令並行壓縮多文件。下面的命令行可以將 /var/log 目錄下所有的擴展名爲 .log 的文件壓縮。通過 xargs 命令同時運行多個 xz 進行壓縮。
# find /var/log -type f -iname "*.log" -print0 | xargs -P4 -n16 xz -T1
注意:運行此命令須有 root 權限。
-
連接 (concatenation) 多個 .xz 文件。可以把多個壓縮數據流連接 (concatenation) 到一個文件中。解壓縮時,就像解壓一個正常單壓縮流文件一樣。如下例,兩個 .xz 文件 concat_1.txt.xz 和 concat_2.txt.xz, 用 cat 命令將他們合併爲文件 concat.xz,解壓縮 concat.xz 這個文件會發現 concat_1.txt.xz 和 concat_2.txt.xz 中的內容合併在一起了。
$ xzcat concat_1.txt.xz .xz file concatenation test: ~ file1 ~ $ xzcat concat_2.txt.xz .xz file concatenation test: ~ file2 ~ $ cat concat_1.txt.xz concat_2.txt.xz > concat.txt.xz #concatenation $ xz -d concat.txt.xz #decompress $ cat concat.txt #print file concat.txt .xz file concatenation test: ~ file1 ~ .xz file concatenation test: ~ file2 ~
-
適於 shell 編程的 Robot 模式。xz 命令使用參數 --robot 輸出易於 shell 處理的信息。
$ xz --list xz_pipe_mini.exe.xz ## normal output Strms Blocks Compressed Uncompressed Ratio Check Filename 2 2 12.8 KiB 42.7 KiB 0.300 CRC64 xz_pipe_mini.exe.xz $ xz --list --robot xz_pipe_mini.exe.xz ## robot model name xz_pipe_mini.exe.xz file 2 2 13112 43695 0.300 CRC64 0 totals 2 2 13112 43695 0.300 CRC64 0 1
下例是利用 --robot 打印出文件壓縮率的一個例子:
$ xz -l --robot xz_pipe_mini.exe.xz | gawk \ '/^name/ {printf "%s =>",$2} \ /^totals/ {printf "compressed radio:%.2f%\n", $6 * 100}' xz_pipe_mini.exe.xz =>compressed radio:30.00%
-
自定義 filter Chain. 有時需要一些特殊的設定,以滿足一些特定需求。xz 提供了很多 參數自定義壓縮細節。下面有兩個例子
-
若希望解壓縮使用很少內存,比如在嵌入式系統中解壓。可以使用使用參數 -e,並設定壓縮使用的字典大小爲 64KB。參數 -e (--extreme) 是指使用稍慢的壓縮速度以嘗試稍稍提高一些壓縮率。這樣便可在解壓縮時使用很少的內存空間。命令如下:
$ xz --check=crc32 --lzma2=preset=6e,dict=64KiB file
-
若壓縮文件中是可執行的二進制數據時,使用的合適的 BCJ(branch/call/jump) filter 可以提高壓縮率。例如 libwx_gtk2u_core-2.8.so.0.8.0 是 wxGTK 庫的一個共享庫文件,它原始的大小爲 3980KB. 若採用默認的參數壓縮後的大小爲 916KB. 通過相應指定 filter Chain, 可以生成更小些的文件 .
$ xz libwx_gtk2u_core-2.8.so.0.8.0 $ du -k libwx_gtk2u_core-2.8.so.0.8.0.xz 916 libwx_gtk2u_core-2.8.so.0.8.0.xz $ xz --x86 --lzma2 libwx_gtk2u_core-2.8.so.0.8.0 $ du -k libwx_gtk2u_core-2.8.so.0.8.0.xz 856 libwx_gtk2u_core-2.8.so.0.8.0.xz
-
若希望解壓縮使用很少內存,比如在嵌入式系統中解壓。可以使用使用參數 -e,並設定壓縮使用的字典大小爲 64KB。參數 -e (--extreme) 是指使用稍慢的壓縮速度以嘗試稍稍提高一些壓縮率。這樣便可在解壓縮時使用很少的內存空間。命令如下:
從上面執行結果來看,利用合適的 filter Chain 最終生成的文件大小爲 865KB。參數 --x86 --lzma2 就組成了一個 filter Chain。參數 --x86 是指使用 x86 平臺的 BCJ filter,適用於 32 位和 64 位系統。參數 --lzma2 則指使用 lzma2 算法進行壓縮。
使用 xzgrep,xzcat,xzdiff 等實用工具
xzcat
xzcat 命令其相當於 "xz --decompress --stdout" 就是將解壓出的數據輸出到標準輸出 (stdout)。下面的例子用來統計壓縮 test.txt.xz 文件所包含數據的行數。
$ xzcat test.txt.xz | wc -l ## count the lines of test.txt
xzgrep
xzgrep 可以用來 grep 壓縮文件所包含的數據內容。其內部就是調用 grep 命令,所以參數和 grep 一致。下面的例子打印出 xz_pipe_comp_mini.c.xz 中所有包含以"lzma_"開頭單詞的行號和行內容。例:
$ xzgrep -P -n "lzma_\w+" xz_pipe_comp_mini.c.xz 8: lzma_check check = LZMA_CHECK_CRC64; 9: lzma_stream strm = LZMA_STREAM_INIT; /* alloc and init lzma_stream struct */ 15: lzma_easy_encoder (&strm, 6, LZMA_CHECK_CRC64); 28: lzma_action action = in_finished ? LZMA_FINISH : LZMA_RUN; 35: lzma_code (&strm, action); /* compress data */ 42: lzma_end (&strm);
xzless 和 xzmore
這兩個命令可以分頁查看壓縮文件中的內容。簡單的來說,相當於
"xzcat <file.xz> | less" 或 "xzcat <file.xz> | more"
xzdiff 和 xzcmp
這兩個命令用於比較兩個 .xz 文件。內部分別調用命令 diff 和 cmp 來實現文件內容的比較。例:
$ xzdiff xz_pipe_comp_mini.c.orig.xz xz_pipe_comp_mini.c.xz 20c20 < if (feof (in_file)) --- > if (feof (in_file)) { 21a22 > } $
liblzma API
關於 liblzma API
liblzma API 提供了 LZMA1/LZMA2 算法的實現,並具有類似於 Zlib 的 API 接口。也和 gzip、bzip2 一樣 XZ Utils 是流壓縮格式。通過 liblzma API 接口,可以相對簡單的編程實現對數據的壓縮和解壓縮。
使用 liblzma API 壓縮數據
使用 liblzma 壓縮數據的過程,大致過程如下。
-
定義 lzma_stream 結構體變量並使用 LZMA_STREAM_INIT 初始化,這個 lzma_stream 結構體變量會在整個數據壓縮過程中被使用。有點類似於 C 中標準庫中文件處理使用的結構體 FILE 。
lzma_stream strm = LZMA_STREAM_INIT;
-
調用 lzma_easy_encoder 函數做壓縮準備工作。函數的第一參數是 lzma_stream. 結構體變量的指針,第二參數則指明瞭期望壓縮率大小。有效值爲 [0,9], 數字越高壓縮率越高。第三參數是指明數據完整性檢查方法,LZMA_CHECK_CRC64 可以滿足大多數情況需要。當然,還有 LZMA_CHECK_CRC32, LZMA_CHECK_SH256 可供選擇,或者使用 LZMA_CHECK_NONE 不進行 數據完整性檢查。
lzma_easy_encoder (&strm, 6, LZMA_CHECK_CRC64);
-
通過設定 lzma_stream 結構體變量中的 next_in 和 avail_in 字段,指明待壓縮的數據開始 地址和長度。
strm.next_in = in_buf; //input buffer address. strm.avail_in = in_len; //data length.
-
通過設定 lzma_stream 結構體變量中的 next_out 和 avail_out 字段,指明存放壓縮結果 buffer 地址和長度。
strm.next_out = out_buf; //output buffer address strm.avail_out = 4096; //output buffer length.
-
然後通過調用 lzma_code 函數來壓縮數據,或結束壓縮數據。lzma_code 函數有兩個參數,第一個參數是 lzma_stream 指針。而第二個參數用來指明 lzma_code 的動作: LZMA_RUN: 進行數據處理; LZMA_FINISH: 結束數據處理。
lzma_code (&strm, LZMA_RUN); /* compress data */ lzma_code (&strm, LZMA_FINISH); /* Finish operation.*/
-
最後要調用 lzma_end 函數釋放資源,退出。
lzma_end(&strm);
下面是一段完整的示例程序。所示程序從標準輸入 (stdin) 讀入數據,壓縮後寫到標準輸出 (stdout). 爲了更便於理解壓縮過程,示例代碼省略了所有的異常處理和一些其他次要細節。
清單 1. 壓縮數據的代碼示例
#include <stdio.h> #include <stdint.h> #include <inttypes.h> #include <stdbool.h> #include <lzma.h> void xz_compress (FILE *in_file, FILE *out_file) { lzma_check check = LZMA_CHECK_CRC64; /* alloc and init lzma_stream struct */ lzma_stream strm = LZMA_STREAM_INIT; uint8_t in_buf [4096]; uint8_t out_buf [4096]; bool in_finished = false; /* initialize xz encoder */ lzma_easy_encoder (&strm, 6, LZMA_CHECK_CRC64); while (! in_finished) { /* read incoming data */ size_t in_len = fread (in_buf, 1, 4096, in_file); if (feof (in_file)) { in_finished = true; } strm.next_in = in_buf; strm.avail_in = in_len; /* if no more data from in_buf, flushes the internal xz buffers and * closes the xz data with LZMA_FINISH */ lzma_action action = in_finished ? LZMA_FINISH : LZMA_RUN; /* loop until there's no pending compressed output */ do { strm.next_out = out_buf; strm.avail_out = 4096; lzma_code (&strm, action); /* compress data */ size_t out_len = 4096 - strm.avail_out; fwrite (out_buf, 1, out_len, out_file); /* write compressed data */ }while (strm.avail_out == 0); } lzma_end (&strm); } int main () { xz_compress (stdin, stdout); return 0; }
編譯命令 :
$ gcc -g -o xz_pipe_mini xz_pipe_comp_mini.c -llzma
使用例子 :
$ cat a.txt | xz_pipe_mini > a.txt.xz
使用 liblzma API 解壓數據
使用 liblzma 進行解壓縮的過程和壓縮數據的過程基本一致。主要的區別是使用 lzma_stream_decoder 函數代替 lzma_easy_decoder 函數來進行 decoder 的初始化。
lzma_stream_decoder (&strm, memory_limit, flags);
lzma_stream_decoder 函數的第一參數是數是 lzma_stream 結構體變量的指針。第二參數則指明瞭在解壓縮過程中使用內存的最大值。若 UINT64_MAX 則爲不限制內存使用量。第三參數用來指明一些其他 flags 值:
- LZMA_TELL_NO_CHECK: 如果壓縮數據流中未指明數據完整性檢查方式,則函數 lzma_code 返回 LZMA_TELL_NO_CHECK 。
- LZMA_TELL_UNSUPPORTED_CHECK: 如果壓縮數據流使用了不被支持的數據完整性檢查方式,則函數 lzma_code 會返回 LZMA_TELL_UNSUPPORTED_CHECK 。
- LZMA_CONCATENATED: 支持多個壓縮流連接到一個 xz 文件內。
下面是一段完整的示例程序。所示程序從標準輸入 (stdin) 讀入數據,解壓縮的數據寫到標準輸出 (stdout). 示例代碼省略了所有的異常處理和一些其他次要細節。
清單 2. 解壓縮數據的代碼示例
#include <stdio.h> #include <stdint.h> #include <inttypes.h> #include <stdbool.h> #include <lzma.h> #define IN_BUF_MAX 4096 #define OUT_BUF_MAX 4096 #define RET_OK 0 #define RET_ERROR 1 int xz_decompress (FILE *in_file, FILE *out_file) { /* alloc and init lzma_stream struct */ lzma_stream strm = LZMA_STREAM_INIT; uint8_t in_buf [IN_BUF_MAX]; uint8_t out_buf [OUT_BUF_MAX]; bool in_finished = false; bool out_finished = false; lzma_action action; lzma_ret ret_xz; int ret; ret = RET_OK; /* initialize xz decoder */ ret_xz = lzma_stream_decoder (&strm, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK|LZMA_CONCATENATED ); if (ret_xz != LZMA_OK) return RET_ERROR; while ((! in_finished) && (! out_finished)) { /* read incoming data */ size_t in_len = fread (in_buf, 1, IN_BUF_MAX, in_file); if (feof (in_file)) in_finished = true; strm.next_in = in_buf; strm.avail_in = in_len; /* if no more data from in_buf, flushes the internal xz buffers and closes the decompressed data with LZMA_FINISH */ action = in_finished ? LZMA_FINISH : LZMA_RUN; /* loop until there's no pending decompressed output */ do { /* out_buf is clean at this point */ strm.next_out = out_buf; strm.avail_out = OUT_BUF_MAX; /* decompress data */ ret_xz = lzma_code (&strm, action); if ((ret_xz != LZMA_OK) && (ret_xz != LZMA_STREAM_END)) { out_finished = true; ret = RET_ERROR; } else { /* write decompressed data */ size_t out_len = OUT_BUF_MAX - strm.avail_out; fwrite (out_buf, 1, out_len, out_file); if (ferror (out_file)) { out_finished = true; ret = RET_ERROR; } } } while (strm.avail_out == 0); } lzma_end (&strm); return ret; } int main () { return xz_decompress (stdin, stdout); }
編譯命令:
$ gcc -g -o xz_pipe_decomp_mini xz_pipe_decomp_mini.c -llzma
使用例子:
$ cat a.txt.xz | xz_pipe_decomp_mini > a.txt
結束語
本文着重從實用的角度介紹了 XZ Utils 的主要部分,還有一些內容由於篇幅的原因,沒有提及。如果您想更深入的研究 XZ Utils,可以訪問 XZ Utils 的官方網站,那裏可以找到最新的源代碼和更詳盡的文檔。
原文轉載自:http://www.ibm.com/developerworks/cn/linux/l-lo-xzutils/