为什么使用 Python 的 tar 库时 tar.xz 文件比 macOS tar 小 15 倍?

为什么使用 Python 的 tar 库时 tar.xz 文件比 macOS tar 小 15 倍?

语境

我正在压缩大约 1.3 GB 的文件夹,每个文件夹包含 1440 个 JSON 文件,发现使用该tar命令和 Python 内置命令有 15 倍的差异tarfilemacOS 上的库或Raspbian 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,其中 是nonename或 之一inode
默认值为--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 进行 tarring,可以进一步证明排序对最终存档大小的影响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 中实现。

相关内容