天天看點

Python資料壓縮和存檔——zlib/gzip/bzip2/lzma/zip/tar

Python資料壓縮和存檔——zlib/gzip/bzip2/lzma/zip/tar

前言

python 中提供了幾種重要的資料壓縮算法的支援,包括 zlib、gzip、bzip2 和 lzma 資料壓縮算法,同時支援建立 ZIP 和 tar 格式的歸檔檔案,這些歸檔格式也同樣支援這些壓縮算法的搭配使用。

具體可見​​https://docs.python.org/3/library/archiving.html​​

這裡需要注意和強調的是,前四種指代的是資料壓縮算法,是針對單一資料(例如檔案、字元串等等)進行的壓縮,而後面兩種則是将一堆資料打包成一個單一的檔案。這一打包過程是支援使用特定的資料壓縮算法對其中的資料進行壓縮的。

# -*- coding: utf-8 -*-
import bz2
import datetime
import gzip
import io
import lzma
import os
import shutil
import subprocess
import sys
import tarfile
import zipfile
import zlib      

zlib

def zlib_compression(src_file=None, tgt_file=None, new_src_file=None, is_stream=False):
    """
    zlib: https://docs.python.org/3/library/zlib.html

    There are known incompatibilities between the Python module and versions of the zlib library
    earlier than 1.1.3; 1.1.3 has a security vulnerability, so we recommend using 1.1.4 or later.
    """
    print(f"構模組化塊時所用的 zlib 庫的版本: {zlib.ZLIB_VERSION}")
    print(f"解釋器所加載的 zlib 庫的版本: {zlib.ZLIB_RUNTIME_VERSION}")

    if src_file is None and tgt_file is None and new_src_file is None:
        print("使用zlib壓縮和解壓字元串")
        data = b"Lots of content here"
        comped_data = zlib.compress(data)
        decomped_data = zlib.decompress(comped_data)
        print(decomped_data)

        # Data compression ratio
        comp_ratio = len(data) / len(comped_data)
        print(f"壓縮率為:{comp_ratio}")
        # Check equality to original object after round-trip
        print(f"解壓後與原始内容一緻:{decomped_data == data}")
    else:
        print("使用zlib壓縮和解壓檔案")
        assert tgt_file.endswith('.zlib')
        if not is_stream:
            with open(src_file, 'rb') as src, open(tgt_file, 'wb') as tgt:
                # 參數 level 為壓縮等級,是整數,可取值為 0 到 9 或 -1。
                # 1 (Z_BEST_SPEED) 表示最快速度和最低壓縮率
                # 9 (Z_BEST_COMPRESSION) 表示最慢速度和最高壓縮率
                # 0 (Z_NO_COMPRESSION) 表示不壓縮。參數預設值為 -1 (Z_DEFAULT_COMPRESSION)。
                # Z_DEFAULT_COMPRESSION 是速度和壓縮率之間的平衡 (一般相當于設壓縮等級為 6)。
                data = src.read()
                tgt.write(zlib.compress(data, level=9))

                hash_code = zlib.adler32(data) & 0xffffffff
                print(hash_code)

            with open(tgt_file, 'rb') as src, open(new_src_file, 'wb') as tgt:
                data = zlib.decompress(src.read())
                tgt.write(data)

                hash_code = zlib.adler32(data) & 0xffffffff
                print(hash_code)
        else:
            with open(src_file, "rb") as src, open(tgt_file, "wb") as tgt:
                # 傳回一個壓縮對象,用來按照資料流的形式進行壓縮,可以避免一次性占用太多的記憶體。
                comp_obj = zlib.compressobj(level=9)

                # https://github.com/rucio/rucio/blob/82a4fe7f3b12120c5815fc1f6b6d231f24949268/lib/rucio/common/utils.py#L284-L306
                hash_code = 1
                while data := src.read(1024):
                    hash_code = zlib.adler32(data, hash_code)

                    # comp_obj.compress: 壓縮 data 并傳回 bytes 對象,
                    # 這個對象含有 data 的部分或全部内容的已壓縮資料。
                    # 所得的對象必須拼接在上一次調用 compress() 方法所得資料的後面。
                    # 緩沖區中可能留存部分輸入以供下一次調用。
                    comped_data = comp_obj.compress(data)
                    tgt.write(comped_data)

                # comp_obj.flush: 壓縮所有緩沖區的資料并傳回已壓縮的資料。
                comped_data = comp_obj.flush()
                tgt.write(comped_data)
                print(hash_code & 0xffffffff)

            with open(tgt_file, 'rb') as src, open(new_src_file, 'wb') as tgt:
                comp_obj = zlib.decompressobj()
                hash_code = 1
                while data := src.read(1024):
                    decomped_data = comp_obj.decompress(data)
                    tgt.write(decomped_data)

                    hash_code = zlib.adler32(decomped_data, hash_code)

                decomped_data = comp_obj.flush()
                tgt.write(decomped_data)

                hash_code = zlib.adler32(decomped_data, hash_code)
                print(hash_code & 0xffffffff)

zlib_compression()
zlib_compression(src_file='./data/src/測試/夕小瑤AI全棧手冊/目錄截圖.jpg',
                 tgt_file='./data/tgt/目錄截圖.zlib',
                 new_src_file="./data/tgt/目錄截圖.jpg",
                 is_stream=True)      

gzip

def gzip_compression(src_file=None, tgt_file=None, new_src_file=None, use_copy=None,
                     is_stream=None):
    """
    gzip: https://docs.python.org/3/library/gzip.html

    此子產品提供的簡單接口幫助使用者壓縮和解壓縮檔案,功能類似于 GNU 應用程式 gzip 和 gunzip。

    資料壓縮由 zlib 子產品提供。
    gzip 子產品提供 GzipFile 類和 open()、compress()、decompress() 幾個便利的函數。
    GzipFile 類可以讀寫 gzip 格式的檔案,還能自動壓縮和解壓縮資料,
    這讓操作壓縮檔案如同操作普通的 file object 一樣友善。
    """
    if src_file is None and tgt_file is None and new_src_file is None:
        print("使用gzip壓縮和解壓字元串")
        data = b"Lots of content here"
        comped_data = gzip.compress(data=data)
        decomped_data = gzip.decompress(data=comped_data)
        print(decomped_data)

        # Data compression ratio
        comp_ratio = len(data) / len(comped_data)
        print(f"壓縮率為:{comp_ratio}")
        # Check equality to original object after round-trip
        print(f"解壓後與原始内容一緻:{decomped_data == data}")
    else:
        print("使用gzip壓縮和解壓檔案")
        assert tgt_file.endswith('gz')
        with open(src_file, "rb") as src, gzip.open(tgt_file, "wb", compresslevel=9) as tgt:
            if not is_stream:
                if use_copy:
                    shutil.copyfileobj(src, tgt)
                else:
                    tgt.write(src.read())
            else:
                while data := src.read(1024):
                    tgt.write(data)
                tgt.flush()

        with gzip.open(tgt_file, "rb") as src, open(new_src_file, "wb") as tgt:
            if not is_stream:
                if use_copy:
                    shutil.copyfileobj(src, tgt)
                else:
                    tgt.write(src.read())
            else:
                while data := src.read(1024):
                    tgt.write(data)

gzip_compression()
gzip_compression(src_file='./data/src/測試/夕小瑤AI全棧手冊/0.程式設計基礎/7款優秀Vim插件幫你打造完美IDE.pdf',
                 tgt_file='./data/tgt/7款優秀Vim插件幫你打造完美IDE.pdf.gz',
                 new_src_file="./data/tgt/7款優秀Vim插件幫你打造完美IDE.pdf",
                 use_copy=True,
                 is_stream=False)
gzip_compression(src_file='./data/src/測試/夕小瑤AI全棧手冊/0.程式設計基礎/7款優秀Vim插件幫你打造完美IDE.pdf',
                 tgt_file='./data/tgt/7款優秀Vim插件幫你打造完美IDE.pdf.gz',
                 new_src_file="./data/tgt/7款優秀Vim插件幫你打造完美IDE.pdf",
                 use_copy=False,
                 is_stream=True)      

bzip2

def bzip2_compression(src_file=None, tgt_file=None, new_src_file=None, is_stream=False):
    """
    bz2: https://docs.python.org/3/library/bz2.html

    此子產品提供了使用 bzip2 壓縮算法壓縮和解壓資料的一套完整的接口。

    bz2 子產品包含:

    - 用于讀寫壓縮檔案的 open() 函數和 BZ2File 類。
    - 用于增量壓縮和解壓的 BZ2Compressor 和 BZ2Decompressor 類。
    - 用于一次性壓縮和解壓的 compress() 和 decompress() 函數。
    """
    if src_file is None and tgt_file is None and new_src_file is None:
        print("使用bzip2算法壓縮和解壓字元串")
        data = b"Lots of content here"
        comped_data = bz2.compress(data)
        decomped_data = bz2.decompress(comped_data)
        print(decomped_data)

        # Data compression ratio
        comp_ratio = len(data) / len(comped_data)
        print(f"壓縮率為:{comp_ratio}")
        # Check equality to original object after round-trip
        print(f"解壓後與原始内容一緻:{decomped_data == data}")
    else:
        print("使用bzip2算法壓縮和解壓檔案")
        assert tgt_file.endswith('bz2')
        # bz2.open:
        # 對于二進制模式,這個函數等價于 BZ2File 構造器:
        #   BZ2File(filename, mode, compresslevel=compresslevel)。
        # 對于文本模式,将會建立一個 BZ2File 對象,并将它包裝到一個 io.TextIOWrapper 執行個體中,
        #   此執行個體帶有指定的編碼格式、錯誤處理行為和行結束符。
        with open(src_file, "rb") as src, bz2.open(tgt_file, 'wb') as tgt:
            if not is_stream:
                tgt.write(src.read())
            else:
                while data := src.read(1024):
                    tgt.write(data)

        with bz2.open(tgt_file, 'rb') as src, open(new_src_file, 'wb') as tgt:
            if not is_stream:
                tgt.write(src.read())
            else:
                while data := src.read(1024):
                    tgt.write(data)

bzip2_compression()
bzip2_compression(src_file='./data/src/測試/夕小瑤AI全棧手冊/0.程式設計基礎/7款優秀Vim插件幫你打造完美IDE.pdf',
                  tgt_file='./data/tgt/7款優秀Vim插件幫你打造完美IDE.pdf.bz2',
                  new_src_file="./data/tgt/7款優秀Vim插件幫你打造完美IDE.pdf",
                  is_stream=False)
bzip2_compression(src_file='./data/src/測試/夕小瑤AI全棧手冊/0.程式設計基礎/7款優秀Vim插件幫你打造完美IDE.pdf',
                  tgt_file='./data/tgt/7款優秀Vim插件幫你打造完美IDE.pdf.bz2',
                  new_src_file="./data/tgt/7款優秀Vim插件幫你打造完美IDE.pdf",
                  is_stream=True)      

lzma

def lzma_compression(src_file=None, tgt_file=None, new_src_file=None, is_stream=False):
    """
    lzma: https://docs.python.org/3/library/lzma.html

    此子產品提供了可以壓縮和解壓縮使用 LZMA 壓縮算法的資料的類和便攜函數。
    其中還包含支援 xz 工具所使用的 .xz 和舊式 .lzma 檔案格式的檔案接口,以及相應的原始壓縮資料流。

    此子產品所提供了接口與 bz2 子產品的非常類似。
    請注意 LZMAFile 和 bz2.BZ2File 都 不是 線程安全的。
    是以如果你需要在多個線程中使用單個 LZMAFile 執行個體,則需要通過鎖來保護它。
    """
    if src_file is None and tgt_file is None and new_src_file is None:
        print("使用lzma算法壓縮和解壓字元串")
        data = b"Lots of content here"
        comped_data = lzma.compress(data)
        # format 參數指定應當被使用的容器格式。
        # 預設值為 FORMAT_AUTO,它可以解壓縮 .xz 和 .lzma 檔案。
        # 其他可能的值為 FORMAT_XZ, FORMAT_ALONE 和 FORMAT_RAW。
        # https://docs.python.org/zh-cn/3/library/lzma.html#lzma.LZMACompressor
        decomped_data = lzma.decompress(comped_data, format=lzma.FORMAT_AUTO)
        print(decomped_data)

        # Data compression ratio
        comp_ratio = len(data) / len(comped_data)
        print(f"壓縮率為:{comp_ratio}")
        # Check equality to original object after round-trip
        print(f"解壓後與原始内容一緻:{decomped_data == data}")
    else:
        print("使用lzma算法壓縮和解壓檔案")
        assert tgt_file.endswith('.xz') or tgt_file.endswith('.lzma')
        # 除了更加 CPU 密集,使用更高的預設等級來壓縮還需要更多的記憶體(并産生需要更多記憶體來解壓縮的輸出)。
        # 例如使用預設等級 9 時,一個 LZMACompressor 對象的開銷可以高達 800 MiB。
        # 出于這樣的原因,通常最好是保持使用預設預設等級。
        with open(src_file, 'rb') as src, lzma.open(tgt_file, 'wb',
                                                    preset=lzma.PRESET_DEFAULT) as tgt:
            if is_stream:
                tgt.write(src.read())
            else:
                while data := src.read(1024):
                    tgt.write(data)

        with lzma.open(tgt_file, 'rb') as src, open(new_src_file, 'wb') as tgt:
            if is_stream:
                tgt.write(src.read())
            else:
                while data := src.read(1024):
                    tgt.write(data)

lzma_compression()
lzma_compression(src_file='./data/src/測試/夕小瑤AI全棧手冊/0.程式設計基礎/7款優秀Vim插件幫你打造完美IDE.pdf',
                 tgt_file='./data/tgt/7款優秀Vim插件幫你打造完美IDE.pdf.xz',
                 new_src_file="./data/tgt/7款優秀Vim插件幫你打造完美IDE.pdf",
                 is_stream=False)
lzma_compression(src_file='./data/src/測試/夕小瑤AI全棧手冊/0.程式設計基礎/7款優秀Vim插件幫你打造完美IDE.pdf',
                 tgt_file='./data/tgt/7款優秀Vim插件幫你打造完美IDE.pdf.xz',
                 new_src_file="./data/tgt/7款優秀Vim插件幫你打造完美IDE.pdf",
                 is_stream=True)      

zip

def zip_compression(src_root, tgt_file, new_src_root1, new_src_root2, use_cmd):
    """
    zip: https://docs.python.org/3/library/zipfile.html

    ZIP 檔案格式是一個常用的歸檔與壓縮标準。這個子產品提供了建立、讀取、寫入、添加及列出 ZIP 檔案的工具。
    此子產品目前不能處理分卷 ZIP 檔案。可以處理使用 ZIP64 擴充(超過 4 GB 的 ZIP 檔案)的 ZIP 檔案。
    它支援解密 ZIP 歸檔中的加密檔案,但是目前不能建立一個加密的檔案。
    解密非常慢,因為它是使用原生 Python 而不是 C 實作的。
    """
    if not use_cmd:
        src_files = [os.path.join('./data/src/a', x) for x in os.listdir('./data/src/a')]
        # 如果 allowZip64 為 True (預設值) 則當 zipfile 大于 4 GiB 時 zipfile 将建立使用 ZIP64 擴充的ZIP檔案。
        # 如果該參數為 false 則當 ZIP 檔案需要 ZIP64 擴充時 zipfile 将引發異常.
        # compresslevel 形參控制在将檔案寫入歸檔時要使用的壓縮等級。
        # - 當使用 ZIP_STORED 或 ZIP_LZMA 時無壓縮效果。
        # - 當使用 ZIP_DEFLATED 時接受整數 0 至 9 (更多資訊參見 zlib)。
        # - 當使用 ZIP_BZIP2 時接受整數 1 至 9 (更多資訊參見 bz2)。
        with zipfile.ZipFile(tgt_file, 'w',
                             allowZip64=True,
                             compression=zipfile.ZIP_BZIP2,
                             compresslevel=9) as tgt:
            print(f"建立zip檔案{tgt.filename}來打包{src_files}")
            # 如果建立檔案時使用 'w', 'x' 或 'a' 模式并且未向歸檔添加任何檔案就執行了 closed,
            # 則會将适當的空歸檔 ZIP 結構寫入檔案。
            for src_file in src_files:
                # 歸檔名稱arcname應當是基于歸檔根目錄的相對路徑,也就是說,它們不應以路徑分隔符開頭。
                tgt.write(filename=src_file,
                          arcname=os.path.relpath(path=src_file,
                                                  start=os.path.dirname(
                                                      os.path.dirname(src_file))))

        with zipfile.ZipFile(tgt_file, 'r') as src:
            if broken_file := src.testzip() is not None:
                print(f"{tgt_file}中檔案{broken_file}損壞")
            print(f"{tgt_file}中的檔案:")
            src.printdir(file=sys.stdout)  # 也可以是具體的檔案
            # 設定 pwd 為用于提取已加密檔案的預設密碼。
            src.setpassword(pwd=None)

            member_namelist = src.namelist()
            # ZipInfo 類的執行個體會通過 getinfo() 和 ZipFile 對象的 infolist() 方法傳回。
            # 每個對象将存儲關于 ZIP 歸檔的一個成員的資訊。
            first_member_info = src.getinfo(name=member_namelist[0])
            member_infolist = src.infolist()
            print(f"第一個歸檔成員的資訊是否一緻:{first_member_info == member_infolist[0]}")

            print(member_namelist[0])
            # mode should be 'r' to read a file already in the ZIP file,
            # or 'w' to write to a file newly added to the archive.
            with src.open(member_namelist[0], 'r') as tgt:
                print(tgt.read().decode('utf-8')[:5])

            for member_info in member_infolist:
                print(f"如果此歸檔成員是一個目錄則傳回 True, 目錄應當總是以 / 結尾: {member_info.is_dir()}")
                print(f"歸檔中的檔案名稱: {member_info.filename}")
                print(f"上次修改存檔成員的時間和日期: {member_info.is_dir()}")
                print(f"已壓縮資料的大小: {member_info.compress_size}")
                print(f"未壓縮檔案的大小: {member_info.file_size}")
                # 如果一個成員檔案名為絕對路徑,則将去掉驅動器/UNC共享點和前導的(反)斜杠,
                # 例如: ///foo/bar 在 Unix 上将變為 foo/bar,
                # 而 C:\foo\bar 在 Windows 上将變為 foo\bar。
                # 并且一個成員檔案名中的所有 ".." 都将被移除,
                # 例如: ../../foo../../ba..r 将變為 foo../ba..r。
                # 在 Windows 上非法字元 (:, <, >, |, ", ?, and *) 會被替換為下劃線 (_)。
                src.extract(member=member_info, path=new_src_root1, pwd=None)  # pwd 是用于解密檔案的密碼。
            # 警告 絕不要未經預先檢驗就從不可靠的源中提取歸檔檔案。 這樣有可能在 path 之外建立檔案,
            # 例如某些成員具有以 "/" 開始的檔案名或帶有兩個點号 ".." 的檔案名。
            # 此子產品會嘗試防止這種情況。
            # 參見 extract() 的注釋。
            src.extractall(path=new_src_root2, members=member_infolist, pwd=None)

        with zipfile.ZipFile(tgt_file, 'a') as src:
            if broken_file := src.testzip() is not None:
                print(f"{tgt_file}中檔案{broken_file}損壞")
            print(f"輸出檔案内容目錄:{src.printdir()}")
            first_member_info = src.infolist()[0]
            print(first_member_info.filename)

            with src.open('c/' + first_member_info.filename, 'w', force_zip64=True) as tgt:
                # 這一操作也是用于添加文檔到存檔中
                tgt.write(
                    f"{datetime.datetime.now()}寫入到檔案{first_member_info.filename}測試".encode(
                        'utf-8'))
    else:
        # zipfile的存檔中成員的自動化命名規則更加合理,即直接以給定的源檔案目錄的作為存檔内成員的根目錄
        # 但是tar卻并非如此,而是直接按照後續給定的原檔案目錄的根目錄作為存檔内成員的根目錄
        cmd = f'python -m zipfile -c {tgt_file} {src_root}'
        print("以相對路徑指定的源檔案目錄層級作為成員名字的根目錄來進行打包:", cmd)
        subprocess.run(args=cmd, shell=True)

        cmd = f'python -m zipfile -l {tgt_file}'
        print("列出存檔中的檔案:", cmd)
        subprocess.run(args=cmd, shell=True)

        src_root = os.path.abspath(src_root)
        cmd = f'python -m zipfile -c {tgt_file} {src_root}'
        print("以絕對路徑指定的源檔案目錄層級作為成員名字的根目錄來進行打包:", cmd)
        subprocess.run(args=cmd, shell=True)

        cmd = f'python -m zipfile -l {tgt_file}'
        print("列出存檔中的檔案:", cmd)
        subprocess.run(args=cmd, shell=True)

        cmd = f'python -m zipfile -e {tgt_file} {new_src_root1}'
        print("解壓存檔到指定目錄:", cmd)
        subprocess.run(args=cmd, shell=True)

        cmd = f'python -m zipfile -t {tgt_file}'
        print("測試存檔是否正常:", cmd)
        subprocess.run(args=cmd, shell=True)

zip_compression(
    src_root='./data/src/a',
    tgt_file='./data/tgt/a.zip',
    new_src_root1='./data/tgt/a',
    new_src_root2='./data/tgt/b',
    use_cmd=False
)
zip_compression(
    src_root='./data/src/a',
    tgt_file='./data/tgt/a.zip',
    new_src_root1='./data/tgt/a',
    new_src_root2='./data/tgt/b',
    use_cmd=True
)      

tar

def tar_compression(start_root, src_root, tgt_file, new_src_root1, new_src_root2, use_cmd):
    """
    tar: https://docs.python.org/3/library/tarfile.html

    tarfile 子產品可以用來讀寫 tar 歸檔,包括使用 gzip, bz2 和 lzma 壓縮的歸檔。
    請使用 zipfile 子產品來讀寫 .zip 檔案,或者使用 shutil 的高層級函數。
    """
    if not use_cmd:
        src_files = [os.path.join(src_root, x) for x in sorted(os.listdir(src_root))]
        with tarfile.open(tgt_file, "w") as tar:
            for src_file in src_files:
                # 将檔案 name 添加到歸檔。
                # name 可以為任意類型的檔案(目錄、fifo、符号連結等等)。
                # 如果給出 arcname 則它将為歸檔中的檔案指定一個替代名稱。
                # 預設情況下會遞歸地添加目錄。這可以通過将 recursive 設為 False 來避免。遞歸操作會按排序順序添加條目。
                tar.add(src_file, arcname=os.path.relpath(src_file, start=start_root),
                        recursive=True)

        with tarfile.open(tgt_file, "r") as tar:
            print(f"tar.list 存檔中的檔案清單:")
            tar.list(verbose=True)
            # getnames内部已經執行了一遍next(),是以該上下文中不能再用next獲得有效資訊了
            member_names = tar.getnames()
            print(f"存檔中的成員名字:{member_names}")
            first_member_name = member_names[0]
            first_member = tar.getmember(name=first_member_name)
            assert first_member == tar.getmember(name=first_member_name)

        with tarfile.open(tgt_file, "r") as tar:
            print(f"tar.next 存檔中的檔案清單:")
            next_member = tar.next()
            while next_member is not None:
                print(next_member.name)
                next_member = tar.next()

        # 絕不要未經預先檢驗就從不可靠的源中提取歸檔檔案。
        # 這樣有可能在 path 之外建立檔案,例如某些成員具有以 "/" 開始的絕對路徑檔案名或帶有兩個點号 ".." 的檔案名。
        with tarfile.open(tgt_file, 'r') as tar:
            members = tar.getmembers()
            for next_member in members:
                print(f"正在提取:{next_member.name} {next_member.size}")
                if next_member.isfile() or next_member.issym() or next_member.islnk():
                    member_fileobj: io.BufferedReader = tar.extractfile(member=next_member)
                    with open(os.path.join(new_src_root1, next_member.name), mode='wb') as f:
                        f.write(member_fileobj.read())
                else:
                    # extract() 方法不會處理某些提取問題。 在大多數情況下你應當考慮使用 extractall() 方法。
                    tar.extract(member=next_member, path=new_src_root1)

        with tarfile.open(tgt_file, "r") as tar:
            # 如果給定了可選的 members,則它必須為 getmembers() 所傳回的清單的一個子集。
            tar.extractall(path=new_src_root2, members=None)
    else:
        # 在直接使用終端指令處理時,tar存檔中存放的成員名字均是實際基于存檔檔案名後跟的目錄進行擴充的
        # 是以使用終端指令最好就是直接在要打包的目錄或者檔案處執行打包。
        # 如果想要将存檔生成到他處,可以對存檔路徑進行修改。
        tgt_file = os.path.abspath(tgt_file)
        new_src_root1 = os.path.abspath(new_src_root1)

        cmd = f'python -m tarfile -c {tgt_file} {new_src_root1}'
        print("打包絕對路徑上的檔案、目錄到絕對路徑上的存檔:", cmd)
        subprocess.run(cmd, shell=True)

        cmd = f'python -m tarfile -l {tgt_file}'
        print("列出絕對路徑上的存檔裡的成員:", cmd)
        subprocess.run(cmd, shell=True)

        cmd = f'cd {os.path.dirname(new_src_root1)} && python -m tarfile -c {tgt_file} {new_src_root1}'
        print("在源檔案目錄的同級目錄下打包絕對路徑上的源檔案目錄到絕對路徑上的存檔:", cmd)
        subprocess.run(cmd, shell=True)

        cmd = f'python -m tarfile -l {tgt_file}'
        print("列出絕對路徑上的存檔裡的成員:", cmd)
        subprocess.run(cmd, shell=True)

        cmd = f'cd {os.path.dirname(new_src_root1)} && python -m tarfile -c {tgt_file} {os.path.basename(new_src_root1)}'
        print("在源檔案目錄的同級目錄下打包相對路徑上的源檔案目錄到絕對路徑上的存檔:", cmd)
        subprocess.run(cmd, shell=True)

        cmd = f'python -m tarfile -l {tgt_file}'
        print("列出絕對路徑上的存檔裡的成員:", cmd)
        subprocess.run(cmd, shell=True)

        cmd = f'python -m tarfile -e {tgt_file} {new_src_root2}'
        print("提取絕對路徑上的存檔到絕對路徑上的目标檔案夾:", cmd)
        subprocess.run(cmd, shell=True)

tar_compression(
    start_root='./data/src',
    src_root='./data/src/a',
    tgt_file='./data/tgt/a.tar',
    new_src_root1='./data/tgt/a',
    new_src_root2='./data/tgt/b',
    use_cmd=False,
)
tar_compression(
    start_root='./data/src',
    src_root='./data/src/a',
    tgt_file='./data/tgt/a.tar',
    new_src_root1='./data/tgt/a',
    new_src_root2='./data/tgt/b',
    use_cmd=True,
)      

參考連結

  • ​​https://docs.python.org/3/library/archiving.html​​
  • 完整的本文代碼可見:​​https://github.com/lartpang/CodeForArticle/blob/main/CompressionAndArchive.Python/main.py​​