BZIP2 檔案格式
bzip2 壓縮的資料,使用 Burrows-Wheeler 區塊排序演算法。
- MIME 類型
- application/x-bzip2
- 副檔名
- .bz2
- 分類
- 壓縮與封存
Magic number(檔案簽章)
| 0x00000000 | 425A68 | BZh | 'BZh' header |
關於 BZIP2 格式
bzip2 用 Burrows-Wheeler 轉換,壓縮率比 gzip 好但速度較慢,在 Unix 的原始碼 tarball 上很常見。標頭是 'BZh' 後面接著一位數的區塊大小。那四個位元組之後,這個檔案就完全不再以位元組為單位了:區塊、霍夫曼表和編碼符號是一個位元一個位元打包上去的,完全沒有對齊 — 這就是為什麼只有第一個區塊落在位元組邊界上,也是為什麼 hex 編輯器看得到 bzip2 檔案的開頭,卻看不懂它的中段。補償是每個區塊互相獨立,所以損壞被關在局部,而且真的救得回來。
檔頭結構,一個位元組一個位元組看
| 位移 | 長度 | 欄位 | 意義 |
|---|---|---|---|
| 0 | 2 | 'BZ' magic | 42 5A。兩個可列印的字母,這在二進位格式裡不太尋常,也是為什麼 'BZh' 在 hex dump 裡會以看得懂的文字出現。 |
| 2 | 1 | Version byte | 'h'(0x68),代表霍夫曼編碼。從 0.9.0 以來這個工具就只產出這一個版本,而解碼器也不接受別的 — 所以那個認得出來的三位元組前綴,其實是 magic 加版本,不只是 magic。 |
| 3 | 1 | Block-size digit | 一個 ASCII 數字 '1' 到 '9',以 100 kB 為單位給出區塊大小 — '9' 是 0x39,代表 900 kB,也是預設值。它決定壓縮、更重要的是解壓縮時的記憶體用量,所以寫入端選定一次之後,每個讀取端都必須照辦。 |
| 4 | 6 | First block magic | 31 41 59 26 53 59,也就是圓周率的位數以壓縮十進位打包。它只有在這裡、也就是第一個區塊的開頭是位元組對齊的;後面每個區塊的同一組值,都落在前一個區塊剛好結束的那個位元位置上。 |
| 10 | 4 | Block CRC-32 | 這個區塊未壓縮內容的檢查碼,一樣只有第一個區塊是位元組對齊的。逐區塊的檢查碼正是讓部分救援有意義的原因:讀取端能明確指出是哪一個區塊壞了。 |
| 14 | variable | Randomised flag and BWT origin pointer | 一個已淘汰的旗標位元(本世紀寫出來的東西一律是 0),後面接一個 24 位元的指標,指向 Burrows-Wheeler 矩陣裡能還原原始順序的那一列。從這裡開始就沒有任何東西是位元組對齊的了:符號表、霍夫曼表、選擇器和編碼資料是一條連續的位元串流。 |
| variable | 6 | End-of-stream magic | 17 72 45 38 50 90,也就是圓周率平方根的位數,標記後面不再有區塊。它落在接近檔尾的地方,但位元位移由它前面所有東西決定,所以它跟檔尾的距離不是固定的。 |
| variable | 4 | Combined CRC-32 | 把每個區塊的 CRC 一路合併起來的值,緊接在結束串流 magic 之後,後面再補零到七個位元,好讓檔案在位元組邊界上收尾。這是 bzip2 唯一的全檔完整性值。 |
怎麼跟長得像的格式分開
| 容易搞混的 | 怎麼分 |
|---|---|
| GZIP | 'BZh' 加一個數字對上 1F 8B。它們在腳本裡的用法完全相同,副檔名也一天到晚被互換,但除此之外毫無共通之處:gzip 是位元組對齊的標頭蓋在一條 deflate 串流上,bzip2 是四個位元組的標頭蓋在一條位元串流上,而且有逐區塊的檢查碼。 |
| 7-Zip | 反方向那個隨時會發生的誤判。7-Zip 自己的 magic 開頭是 37 7A,讀起來就是 '7z',而 7z 封存檔可以拿 bzip2 當內部壓縮方法 — 所以 'BZh' 這個字串會出現在 .7z 檔案裡面。只有位移 0 說了算,而 .7z 那裡是 37 7A BC AF 27 1C。 |
| ZIP | ZIP 的壓縮方法 12 就是 bzip2,所以一個 ZIP 項目的資料,可能在它的區域檔案標頭與變動長度欄位之後立刻以 'BZh' 開頭。只要位移 0 讀到 50 4B 03 04,在檔案裡搜到 'BZh' 就斷定手上是 .bz2 就是錯的。 |
| XZ | 在多數原始碼發佈上取代它的那個格式。XZ 開頭是 FD 37 7A 58 5A 00,壓縮率也更好;bzip2 剩下的優勢是它的區塊彼此獨立,而 xz 的區塊預設不是。 |
| pbzip2/lbzip2 | 平行壓縮器吐出來的是一串完整、彼此獨立的 bzip2 串流串接在一起,所以 'BZh' 會在檔案裡以位元組對齊的位置重複出現好幾次,而圓周率那組區塊 magic 每條串流出現一次。結果仍然是一個合法的 .bz2,一般的解壓縮器從頭讀到尾都沒問題。 |
常見的壞法
- 截斷。切口之前每個完整的區塊都還解得出來,因為區塊之間互不相依。bzip2recover 利用的正是這一點:它在每一組圓周率區塊 magic 的位置把位元串流切開,把每個區塊各自寫成一個小小的 .bz2,所以受損的封存檔可以只賠掉一個區塊的資料。
- 區塊裡翻掉一個位元。因為區塊是霍夫曼編碼的,損壞通常在走到結尾之前就讓解碼脫軌 — 報的是資料錯誤,不是檢查碼不符。真的解到結尾時,位移 10 的逐區塊 CRC 會抓到它,並指出是哪一個區塊壞了。
- 文字模式傳輸。位元串流裡的換行轉換,會讓損壞位元組之後的所有東西整個位移八個位元,所以即使四個位元組的標頭完好、檔案也照樣被正確辨識,解碼還是幾乎立刻就失敗。
- 手改過的區塊大小數字。把 '9' 改成 '1',得到的檔案在每個偵測器眼裡仍然像 bzip2,卻會在第一個區塊就失敗,因為解壓縮器是靠那一個位元組決定緩衝區大小的。
- 串接起來的串流被截斷在中途。平行壓縮的封存檔是一個檔案裡好幾條串流;掉了尾巴只會掉最後一條,前面那些都解得乾乾淨淨 — 這就是為什麼下載到一半的 pbzip2 檔案,往往還是能吐出大部分內容。
這個格式現在還在用嗎
bzip2 的高光時刻大約是 1999 到 2010 年,那時它是原始碼 tarball 從 gzip 往上升級的明顯選項,而網路上大量典藏的軟體至今仍是 .tar.bz2 — 光這一點就保證解碼器會一直出貨下去。要用在新工作上,它兩邊被夾殺:xz 在相近甚至更好的解壓縮速度下明顯壓得更小,而 Zstandard 在 gzip 等級的壓縮率下壓得快得多,所以各家發行版好幾年前就轉走了。它上游也沒有動能;原始實作實質上有十年沒人維護。真正還替它說話的是那個區塊結構。區塊彼此獨立,代表壓縮用 pbzip2 就能輕鬆平行化,而且在常見的 Unix 壓縮工具裡獨一無二的是:受損的封存檔可以一個區塊一個區塊搶救,而不是碰到第一個壞位元組就整份報銷。
在這個站上打開 BZIP2 檔
這些全部在你的瀏覽器裡跑,檔案不會離開你的電腦。
相關格式
容易和 BZIP2 搞混的格式,可以比對它們的開頭位元組。