macOS tar에 비해 Python의 tar 라이브러리를 사용할 때 tar.xz 파일이 15배 더 작은 이유는 무엇입니까?

macOS tar에 비해 Python의 tar 라이브러리를 사용할 때 tar.xz 파일이 15배 더 작은 이유는 무엇입니까?

문맥

각각 1440개의 JSON 파일로 채워진 ~1.3GB 폴더를 압축하고 있으며 명령을 사용하는 것과 tarPython에 내장된 것 사이에 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

제가 사용하는 버전은 다음과 같습니다.

  • tarmacOS에서:bsdtar 3.3.2 - libarchive 3.3.2 zlib/1.2.11 liblzma/5.0.5 bz2lib/1.0.6
  • tarRaspbian 10에서:xz (XZ Utils) 5.2.4 liblzma 5.2.4
  • tarfile파이썬 라이브러리: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

짧은 대답: 예, Python을 사용하여 데이터를 압축하는 것이 안전하며 tarlibBSD에 비해 손실되는 것은 없습니다 tar.

근본적인 문제: 정렬

내 생각에 근본적인 문제는 정렬 옵션이 없는 BSD tar와 GNU가 tar파일을 정의되지 않은 순서로 아카이브에 넣는다는 것입니다.

GNU에는 다음과 같은 옵션이 tar있습니다 --sort.

, 또는 ORDER중 하나에 따라 디렉토리 항목을 정렬합니다 . 기본값은 운영 체제에서 반환한 것과 동일한 순서로 아카이브 멤버를 저장하는 입니다.nonenameinode
--sort=none

GNU 테스트tar

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.5MB로 Python 라이브러리에서 생성된 아카이브 크기와 동일합니다.

정렬된 순서로 연결

정렬이 최종 아카이브 크기에 미치는 영향은 먼저 이름별로 정렬된 모든 JSON 파일(처음에 생성 unixtime이 있음)을 연결한 다음 BSD로 tar링하여 추가로 보여줍니다 tar.

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

아카이브 zsh-cat-archive.tar.xz도 1.5MB입니다.

파이썬 tarfile정렬

마지막으로,Python TarFile.add함수 문서Python이 tarfile기본적으로 정렬하는지 확인합니다.

디렉터리는 기본적으로 재귀적으로 추가됩니다. 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

그리고 그렇습니다. 이러한 예 xz에서는 xz.

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

압축 xz수준의 범위는 ~이며 -0기본값 -9은 입니다 -6. 따라서 -9압축 수준이 가장 높습니다.

xz기본적으로 macOS에는 설치되어 있지 않습니다 . macOS에 설치하려면 먼저 설치해야 합니다.홈브류그런 다음 설치xzHomebrew를 통해 다음과 같이 하세요:

brew install xz

답변3

Python이 압축에 무엇을 사용하는지 궁금합니다.

http://tukaani.org/xz/

아마도 liblzma의 함수 호출을 사용하고 있을 것입니다.타르아마도 xz 쉘 명령을 통해 파이핑 중일 것입니다.

다음에 대한 간단한 의견 --sort=name:

정렬 옵션은 GNU tar에 대한 비교적 최근의 향상된 기능이며 tar 버전 1.28에 도입되었습니다.

BSD tar에서는 결코 구현되지 않을 수 있습니다.

관련 정보