與 macOS tar 相比,為什麼使用 Python 的 tar 函式庫時 tar.xz 檔案小 15 倍?

與 macOS tar 相比,為什麼使用 Python 的 tar 函式庫時 tar.xz 檔案小 15 倍?

情境

我正在壓縮大約 1.3 GB 的資料夾,每個資料夾包含 1440 個 JSON 文件,發現使用該tar命令與 Python 的內建命令之間存在 15 倍的差異tarfilemacOS 上的庫或樹莓派 10(巴斯特)

最小工作範例

該腳本比較了兩種方法:

#!/usr/bin/env python3

from pathlib import Path
from subprocess import call
import tarfile

fullpath = Path("/Users/user/Desktop/temp/tar/2021-03-11")
zsh_out = Path(fullpath.parent, "zsh-archive.tar.xz")
py_out = Path(fullpath.parent, "py-archive.tar.xz")

# tar using terminal
# tar cJf zsh-archive.tar.xz folderpath
call(["tar", "cJf", zsh_out, fullpath])

# tar using tarfile library
with tarfile.open(py_out, "w:xz") as tar:
    tar.add(fullpath, arcname=fullpath.stem)

# Print filesizes
print(f"zsh tar filesize: {round(Path(zsh_out).stat().st_size/(1024*1024), 2)} MB")
print(f"py tar filesize: {round(Path(py_out).stat().st_size/(1024*1024), 2)} MB")

輸出是:

zsh tar filesize: 23.7 MB
py tar filesize: 1.49 MB

我使用的版本如下:

  • tar在 macOS 上:bsdtar 3.3.2 - libarchive 3.3.2 zlib/1.2.11 liblzma/5.0.5 bz2lib/1.0.6
  • tar在 Raspbian 10 上:xz (XZ Utils) 5.2.4 liblzma 5.2.4
  • tarfilePython 函式庫:0.9.0

我嘗試過的事情

壓縮後,我提取了兩個檔案並將生成的資料夾與以下內容進行比較:

diff -r py-archive-expanded zsh-archive-expanded

沒有什麼差別。

如果我直接比較兩個 tar 檔案,它們看起來不同:

➜ diff zsh-archive.tar.xz py-archive.tar.xz
Binary files zsh-archive.tar.xz and py-archive.tar.xz differ

如果我使用 Quicklook(和 Betterzip 外掛程式)檢查檔案,我會發現檔案中的文件以不同的方式排序:

左邊是zsh-archive.tar.xz,右邊是py-archive.tar.xz

在此輸入影像描述在此輸入影像描述

zsh 檔案使用未知順序,而 Python 檔案則會依修改日期對檔案進行排序。我不確定這是否重要。

問題

到底是怎麼回事?使用 Python 庫壓縮資料會丟失一些東西嗎? 15 倍的大小差異是否表示存在某些問題?或者我可以安全地繼續使用高效能的 Python 實作嗎?

答案1

簡短的回答:是的,使用 Pythontarlib壓縮資料是安全的,與 BSD 相比沒有任何損失tar

根本問題:排序

我認為根本問題是 BSDtar和 GNUtar沒有任何排序選項,以未定義的順序將檔案放入存檔中。

GNUtar有一個--sort選項:

根據 排序目錄條目,它是、或ORDER之一。 預設值為,它會按照作業系統傳回的順序儲存存檔成員。nonenameinode
--sort=none

測試 GNUtar

為了測試這一點,我tar在我的 Mac 上安裝了 GNU:

brew install gnu-tar

然後對同一個資料夾進行 tar 壓縮,但使用以下--sort選項:

gtar --sort='name' -cJf zsh-archive-sorted.tar.xz /Users/user/Desktop/temp/tar/2021-03-11

zsh-archive-sorted.tar.xz存檔大小為 1.5 MB,等於 Python 庫所建立的存檔的大小。

按排序順序連接

透過先連接所有按名稱排序的 JSON 檔案(其開頭有建立 unixtime),然後使用 BSD 壓縮,進一步證明了排序對最終存檔大小的影響tar

cat *.json > all.txt
tar cJf zsh-cat-archive.tar.xz all.txt

檔案zsh-cat-archive.tar.xz也有 1.5 MB。

Pythontarfile排序

最後,PythonTarFile.add函數的文檔確認 Pythontarfile預設排序:

預設情況下,目錄是遞歸添加的。將 recursive 設為 False 可以避免這種情況。遞歸按排序順序新增條目。

為什麼排序很重要

我認為排序對我的案例有如此影響的原因如下:

我的 JSON 檔案包含數百輛車輛的位置。我每分鐘都會讀出所有位置,但其中只有少數位置每分鐘都有不同的數值。
透過按名稱對文件進行排序,兩個後續文件之間的字元幾乎沒有不同。顯然這對於壓縮效率非常有利。

答案2

嘗試在 macOS 命令列中設定壓縮等級。

我知道你在問xz但解釋在這個答案在這裡,在舊版的 GZip 上,您可以使用環境變數設定壓縮級別,如下所示:

GZIP=-9 tar cf zsh-archive.tar.xz folderpath

也就是說,這似乎只適用於 GZip 1.8,並且在更高版本中已被棄用。因此,請使用tar 的-I/選項;--use-compress-program=COMMAND請注意,此選項可能不適用於 macOS,但為了以防萬一,還是放在這裡。因此命令將更改為:

tar -I 'gzip -9' -cf zsh-archive.tar.xz folderpath

是的,這些範例將壓縮存檔 Gzip 而不是xz,但您可以輕鬆地將命令變更為xz這樣使用:

tar -I 'xz -9' -cf zsh-archive.tar.xz folderpath

壓縮xz等級範圍從-0-9,預設為-6;-9最高壓縮等級也是如此。

請注意,xz預設情況下,macOS 上並未安裝。要在 macOS 上安裝它,您必須先安裝自製然後安裝xz透過 Homebrew 像這樣:

brew install xz

答案3

讓我想知道 Python 使用什麼來進行壓縮

http://tukaani.org/xz/

它可能使用 liblzma 中的函數呼叫。柏油可能是透過 xz shell 指令進行管道傳輸。

快速評論--sort=name

排序選項是 GNU tar 的一個相對較新的增強功能,在 tar 版本 1.28 中引入。

它可能永遠不會在 BSD tar 中實現。

相關內容