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

Saa*_*kke 240 compression zsh python tar macos

语境

我正在压缩大约 1.3 GB 的文件夹,每个文件夹都包含 1440 个 JSON 文件,发现在 macOS 或Raspbian 10 (Buster)上使用tar命令和 Python 的内置tarfile库之间存在 15 倍的差异

最小工作示例

此脚本比较了两种方法:

#!/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")

Run Code Online (Sandbox Code Playgroud)

输出是:

zsh tar filesize: 23.7 MB
py tar filesize: 1.49 MB
Run Code Online (Sandbox Code Playgroud)

我使用的版本如下:

  • 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
  • tarfile 蟒库: 0.9.0

我尝试过的事情

压缩后,我提取了两个档案并将生成的文件夹与:

zsh tar filesize: 23.7 MB
py tar filesize: 1.49 MB
Run Code Online (Sandbox Code Playgroud)

没有区别。

如果我直接比较两个 tar 档案,它们似乎不同:

diff -r py-archive-expanded zsh-archive-expanded
Run Code Online (Sandbox Code Playgroud)

如果我使用 Quicklook(和 Betterzip 插件)检查档案,我会看到档案中的文件以不同的方式排序:

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

在此处输入图片说明在此处输入图片说明

zsh 存档使用未知顺序,Python 存档按修改日期对文件进行排序。我不确定这是否重要。

到底是怎么回事?使用 Python 库压缩我的数据是否会丢失一些东西?15 倍的大小差异是否表明存在某些问题?或者我可以安全地继续使用高效的 Python 实现吗?

Saa*_*kke 304

简短回答:是的,使用 Pythontarlib压缩数据是安全的,与 BSD 相比没有任何损失tar

潜在问题:排序

我认为潜在的问题是没有任何排序选项的BSDtar和 GNUtar以未定义的顺序将文件放在存档中。

GNUtar有一个--sort选项:

根据、、 或ORDER之一对目录条目进行排序。 默认为,它按照操作系统返回的相同顺序存储归档成员。nonenameinode
--sort=none

测试 GNU tar

为了测试这个,我tar在我的 Mac 上安装了 GNU :

brew install gnu-tar
Run Code Online (Sandbox Code Playgroud)

然后对同一个文件夹进行tarred,但有以下--sort选项:

gtar --sort='name' -cJf zsh-archive-sorted.tar.xz /Users/user/Desktop/temp/tar/2021-03-11
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

zsh-cat-archive.tar.xz档案还1.5 MB。

Pythontarfile排序

最后,PythonTarFile.add函数文档确认 Pythontarfile默认排序:

默认情况下以递归方式添加目录。这可以通过将 recursive 设置为 False 来避免。递归按排序顺序添加条目。

为什么排序很重要

我认为排序对我的情况有如此影响的原因如下:

我的 JSON 文件包含数百辆车的位置。每分钟我都会读出所有位置,但只有少数这些位置每分钟都有不同的值。
通过按名称对文件进行排序,两个后续文件之间几乎没有不同的字符。显然这对压缩效率非常有利。

  • 哇,另一种排序使事情变得更快的情况。 (57认同)
  • TL:DR:“未排序”意味着按照我们从操作系统的系统调用中获取它们的顺序使用 dir 条目,您可以使用 `ls -U` 看到它们。 (27认同)
  • 哇!你知道,这在如此基本的层面上非常有意义,我赞扬你发现这一点。以某种方式对文本文件进行排序可以提高压缩率的想法在陈述时似乎非常明显,但如果没有经验则不明显. 优秀的答案! (19认同)
  • 压缩程序对由单个字典控制的文本块进行操作;通过对输入进行排序,您将相似的位彼此靠近,从而允许 `xz` 在一个字典中压缩大量相似的数据。压缩和解压缩可能也更快。 (10认同)
  • 我还不太明白为什么操作系统使用 sort=none 选项以“未排序”的顺序返回文件。我的意思是,总有某种排序顺序,对吧?如果有人知道操作系统使用什么顺序,请随意添加。 (5认同)
  • @SaaruLindestøkke 操作系统返回目录中文件的顺序取决于所使用的文件系统(假设使用相同的操作系统,显然您可以轻松地修补 linux,以便它默认以您想要的某种顺序返回文件,或者它将默认情况下随机排序)。因此,任何操作系统默认使用 **no** 单一排序顺序,因此我们不提供保证,我们说“不假设任何特定的排序顺序”,这并不意味着文件系统在返回之前主动随机化结果他们,这只是意味着如果用户更改 fs 结果可能会改变 (3认同)
  • @Giacomo1968 这对我来说没有直接意义:相似模式的接近性并不是*本身*了解它们的要求:这可能是字典大小的函数,参见例如 https://superuser.com/questions /616785/how-does-dictionary-size-affect-compression (3认同)
  • 相关:[文本文件中数据的顺序是否影响其压缩率?](/sf/ask/1041691871/) (2认同)

Gia*_*968 6

尝试在 macOS 命令行中设置压缩级别。

我知道您在询问,xz但在此答案对此进行了解释,在旧版本的 GZip 上,您可以使用如下环境变量设置压缩级别:

GZIP=-9 tar cf zsh-archive.tar.xz folderpath
Run Code Online (Sandbox Code Playgroud)

也就是说,它似乎只适用于 GZip 1.8,并且在更高版本中会贬值。所以对 tar使用-I/--use-compress-program=COMMAND选项;请注意,此选项可能不适用于 macOS,但还是放在此处以防万一。所以命令将更改为:

tar -I 'gzip -9' -cf zsh-archive.tar.xz folderpath
Run Code Online (Sandbox Code Playgroud)

是的,这些示例将压缩存档 Gzip 而不是xz,但您可以轻松地将命令更改为此使用xz

tar -I 'xz -9' -cf zsh-archive.tar.xz folderpath
Run Code Online (Sandbox Code Playgroud)

xz压缩级别范围从-0-9与默认之中-6; -9最高压缩级别也是如此。

请注意,xz默认情况下未安装在 macOS 上。要在 macOS 上安装它,您必须先安装Homebrew,然后xz通过 Homebrew安装,如下所示:

brew install xz
Run Code Online (Sandbox Code Playgroud)

  • @Giacomo1968 我刚刚意识到`-I` 选项只是GNU `tar`。它在我的 BSD `tar` 上丢失的事实应该表明有什么东西出了问题。 (2认同)