"tarfile" --- 讀取與寫入 tar 封存檔案
*************************************

**原始碼：**Lib/tarfile.py

======================================================================

"tarfile" 模組用於讀取與寫入 tar 封存檔案，包括使用 gzip、bz2 和 lzma
壓縮的檔案。使用 "zipfile" 模組來讀取或寫入 ".zip" 檔案，或使用 shutil
中的更高階函式。

一些事實和數據：

* 若相對應的模組可用，則可讀取與寫入 "gzip"、"bz2"、"compression.zstd"
  和 "lzma" 壓縮的封存檔。

  如果你的 CPython 副本中缺少這些*選用模組 (optional modules)*，請查閱
  你的發行者（即提供 Python 給你的人）的文件。如果你就是發行者，請參閱
  可選模組的需求。

* 支援 POSIX.1-1988 (ustar) 格式的讀取與寫入。

* 支援 GNU tar 格式的讀取與寫入，包括 *longname* 和 *longlink* 擴充功
  能，以及所有 *sparse* 擴充功能變體的唯讀支援，包括還原稀疏檔案
  (sparse files)。

* 支援 POSIX.1-2001 (pax) 格式的讀取與寫入。

* 處理目錄、一般檔案、硬連結 (hardlinks)、符號連結 (symbolic links)、
  fifos、字元裝置 (character devices) 和區塊裝置 (block devices)，並能
  夠取得與還原檔案資訊，如時間戳記、存取權限和擁有者。

在 3.3 版的變更: 新增對 "lzma" 壓縮的支援。

在 3.12 版的變更: 解壓縮封存檔時會使用篩選器 (filter)，你可以用它來限
制一些意外或危險的功能，或是明確表示這些功能是你預期的，而且封存檔是完
全可信任的。

在 3.14 版的變更: 將預設解壓縮篩選器設為 "data"，這會禁止一些危險的功
能，例如指向絕對路徑或目的地之外路徑的連結。在此之前，篩選器策略等同於
"fully_trusted"。

在 3.14 版的變更: 新增對 Zstandard 壓縮的支援（使用 "compression.zstd"
）。

tarfile.open(name=None, mode='r', fileobj=None, bufsize=10240, **kwargs)

   回傳路徑名稱 *name* 的 "TarFile" 物件。關於 "TarFile" 物件與允許的
   關鍵字引數的詳細資訊，請參閱 TarFile 物件。

   *mode* 必須是 "'filemode[:compression]'" 形式的字串，預設為 "'r'"。
   以下是所有可能的模式組合：

   +--------------------+-----------------------------------------------+
   | 模式               | 操作                                          |
   |====================|===============================================|
   | "'r'" 或 "'r:*'"   | 開啟以讀取，自動偵測壓縮格式（建議使用）。    |
   +--------------------+-----------------------------------------------+
   | "'r:'"             | 開啟以讀取未壓縮的檔案。                      |
   +--------------------+-----------------------------------------------+
   | "'r:gz'"           | 開啟以讀取 gzip 壓縮的檔案。                  |
   +--------------------+-----------------------------------------------+
   | "'r:bz2'"          | 開啟以讀取 bzip2 壓縮的檔案。                 |
   +--------------------+-----------------------------------------------+
   | "'r:xz'"           | 開啟以讀取 lzma 壓縮的檔案。                  |
   +--------------------+-----------------------------------------------+
   | "'r:zst'"          | 開啟以讀取 Zstandard 壓縮的檔案。             |
   +--------------------+-----------------------------------------------+
   | "'x'" 或 "'x:'"    | 建立一個不壓縮的 tarfile（獨佔模式）。如果檔  |
   |                    | 案已存在，則引發 "FileExistsError" 例外。     |
   +--------------------+-----------------------------------------------+
   | "'x:gz'"           | 建立一個使用 gzip 壓縮的 tar 檔案。如果檔案已 |
   |                    | 存在則引發 "FileExistsError" 例外。           |
   +--------------------+-----------------------------------------------+
   | "'x:bz2'"          | 建立一個使用 bzip2 壓縮的 tar 檔案。如果檔案  |
   |                    | 已存在，則引發 "FileExistsError" 例外。       |
   +--------------------+-----------------------------------------------+
   | "'x:xz'"           | 建立一個使用 lzma 壓縮的 tar 檔案。如果檔案已 |
   |                    | 存在，則引發 "FileExistsError" 例外。         |
   +--------------------+-----------------------------------------------+
   | "'x:zst'"          | 建立一個使用 Zstandard 壓縮的 tar 檔案。如果  |
   |                    | 檔案已存在，則引發 "FileExistsError" 例外。   |
   +--------------------+-----------------------------------------------+
   | "'a'" 或 "'a:'"    | 開啟以附加內容且不使用壓縮。如果檔案不存在則  |
   |                    | 建立它。                                      |
   +--------------------+-----------------------------------------------+
   | "'w'" 或 "'w:'"    | 開啟以寫入未壓縮的內容。                      |
   +--------------------+-----------------------------------------------+
   | "'w:gz'"           | 開啟以進行 gzip 壓縮寫入。                    |
   +--------------------+-----------------------------------------------+
   | "'w:bz2'"          | 開啟以進行 bzip2 壓縮寫入。                   |
   +--------------------+-----------------------------------------------+
   | "'w:xz'"           | 開啟以進行 lzma 壓縮寫入。                    |
   +--------------------+-----------------------------------------------+
   | "'w:zst'"          | 開啟以進行 Zstandard 壓縮寫入。               |
   +--------------------+-----------------------------------------------+

   請注意 "'a:gz'"、"'a:bz2'" 或 "'a:xz'" 是不可能的。如果 *mode* 不適
   合開啟特定（壓縮）檔案以進行讀取，則會引發 "ReadError"。使用 *mode*
   "'r'" 可避免此問題。如果不支援某個壓縮方法，則會引發
   "CompressionError"。

   如果指定了 *fileobj*，則它會被用作 *name* 以二進位模式開啟的*檔案物
   件* 的替代方案。它應該位於位置 0。

   對於模式 "'w:gz'"、"'x:gz'"、"'w|gz'"、"'w:bz2'"、"'x:bz2'"、
   "'w|bz2'"，"tarfile.open()" 接受關鍵字引數 *compresslevel*（預設為
   "9"）來指定檔案的壓縮等級。

   對於模式 "'w:xz'"、"'x:xz'" 和 "'w|xz'"，"tarfile.open()" 接受關鍵
   字引數 *preset* 來指定檔案的壓縮等級。

   對於模式 "'w:zst'"、"'x:zst'" 和 "'w|zst'"，"tarfile.open()" 接受關
   鍵字引數 *level* 來指定檔案的壓縮等級。也可以傳遞關鍵字引數
   *options*，提供由 "CompressionParameter" 描述的進階 Zstandard 壓縮
   參數。可以傳遞關鍵字引數 *zstd_dict* 以提供 "ZstdDict"，這是一個用
   於改善較小資料量壓縮效果的 Zstandard 字典。

   對於特殊用途，*mode* 有第二種格式："'filemode|[compression]'"。
   "tarfile.open()" 會回傳一個 "TarFile" 物件，該物件將其資料作為區塊
   串流處理。不會對檔案進行隨機尋找。如果提供了 *fileobj*，它可以是任
   何具有 "read()" 或 "write()" 方法（取決於 *mode*）且能處理位元組的
   物件。*bufsize* 指定區塊大小，預設為 "20 * 512" 位元組。此變體可與
   "sys.stdin.buffer"、socket *檔案物件* 或磁帶裝置等結合使用。但是，
   這樣的 "TarFile" 物件受到限制，因為它不允許隨機存取，請參閱 範例。
   目前可能的模式：

   +---------------+----------------------------------------------+
   | 模式          | 操作                                         |
   |===============|==============================================|
   | "'r|*'"       | 開啟以讀取 tar 區塊的*串流*，自動偵測壓縮格  |
   |               | 式。                                         |
   +---------------+----------------------------------------------+
   | "'r|'"        | 開啟以讀取未壓縮的 tar 區塊*串流*。          |
   +---------------+----------------------------------------------+
   | "'r|gz'"      | 開啟以讀取 gzip 壓縮的*串流*。               |
   +---------------+----------------------------------------------+
   | "'r|bz2'"     | 開啟以讀取 bzip2 壓縮的*串流*。              |
   +---------------+----------------------------------------------+
   | "'r|xz'"      | 開啟以讀取 lzma 壓縮的*串流*。               |
   +---------------+----------------------------------------------+
   | "'r|zst'"     | 開啟以讀取 Zstandard 壓縮的*串流*。          |
   +---------------+----------------------------------------------+
   | "'w|'"        | 開啟以寫入未壓縮的*串流*。                   |
   +---------------+----------------------------------------------+
   | "'w|gz'"      | 開啟以寫入 gzip 壓縮的*串流*。               |
   +---------------+----------------------------------------------+
   | "'w|bz2'"     | 開啟以寫入 bzip2 壓縮的*串流*。              |
   +---------------+----------------------------------------------+
   | "'w|xz'"      | 開啟以寫入 lzma 壓縮的*串流*。               |
   +---------------+----------------------------------------------+
   | "'w|zst'"     | 開啟以寫入 Zstandard 壓縮的*串流*。          |
   +---------------+----------------------------------------------+

   在 3.5 版的變更: 新增了 "'x'"（獨佔建立）模式。

   在 3.6 版的變更: *name* 參數接受*類路徑物件*。

   在 3.12 版的變更: *compresslevel* 關鍵字引數也適用於串流。

   在 3.14 版的變更: *preset* 關鍵字引數也適用於串流。

class tarfile.TarFile

   用於讀取和寫入 tar 封存檔的類別。請勿直接使用此類別：請改用
   "tarfile.open()"。請參閱 TarFile 物件。

tarfile.is_tarfile(name)

   如果 *name* 是一個 "tarfile" 模組可以讀取的 tar 封存檔，則回傳
   "True"。*name* 可以是 "str"、檔案或類檔案物件。

   在 3.9 版的變更: 對檔案和類檔案物件的支援。

"tarfile" 模組定義了以下例外：

exception tarfile.TarError

   所有 "tarfile" 例外的基底類別。

exception tarfile.ReadError

   當開啟的 tar 封存檔無法被 "tarfile" 模組處理或以某種方式無效時引發
   。

exception tarfile.CompressionError

   當壓縮方法不被支援或資料無法正確解碼時引發。

exception tarfile.StreamError

   當遇到類似串流的 "TarFile" 物件的典型限制時引發。

exception tarfile.ExtractError

   在使用 "TarFile.extract()" 時針對*非致命*錯誤引發，但僅限於
   "TarFile.errorlevel""== 2" 時。

exception tarfile.HeaderError

   如果 "TarInfo.frombuf()" 得到的緩衝區無效，則引發此例外。

exception tarfile.FilterError

   被篩選器拒絕的成員的基底類別。

   tarinfo

      篩選器拒絕解壓縮的成員資訊，以 TarInfo 表示。

exception tarfile.AbsolutePathError

   當拒絕解壓縮具有絕對路徑的成員時引發。

exception tarfile.OutsideDestinationError

   當拒絕解壓縮位於目的地目錄之外的成員時引發。

exception tarfile.SpecialFileError

   當拒絕解壓縮特殊檔案（例如裝置或管道）時引發。

exception tarfile.AbsoluteLinkError

   當拒絕解壓縮具有絕對路徑的符號連結時引發。

exception tarfile.LinkOutsideDestinationError

   當拒絕解壓縮指向目的地目錄之外的符號連結時引發。

exception tarfile.LinkFallbackError

   當透過解壓縮另一個封存檔成員來模擬連結（硬連結或符號連結）時，如果
   該成員會被篩選器位置拒絕，則引發此例外。用於拒絕替代成員的例外可作
   為 "BaseException.__context__" 取得。

   在 3.14 版被加入.

以下常數可在模組層級使用：

tarfile.ENCODING

   預設字元編碼：在 Windows 上為 "'utf-8'"，否則為
   "sys.getfilesystemencoding()" 回傳的值。

tarfile.REGTYPE
tarfile.AREGTYPE

   一個普通檔案 "type"。

tarfile.LNKTYPE

   一個連結（在 tar 檔案內）"type"。

tarfile.SYMTYPE

   一個符號連結 "type"。

tarfile.CHRTYPE

   一個字元特殊裝置 "type"。

tarfile.BLKTYPE

   一個區塊特殊裝置 "type"。

tarfile.DIRTYPE

   一個目錄 "type"。

tarfile.FIFOTYPE

   一個 FIFO 特殊裝置 "type"。

tarfile.CONTTYPE

   一個連續檔案 "type"。

tarfile.GNUTYPE_LONGNAME

   一個 GNU tar longname "type"。

tarfile.GNUTYPE_LONGLINK

   一個 GNU tar longlink "type"。

tarfile.GNUTYPE_SPARSE

   一個 GNU tar 稀疏檔案 "type"。

以下每個常數定義了 "tarfile" 模組能夠建立的 tar 封存檔格式。詳細資訊請
參閱 支援的 tar 格式 一節。

tarfile.USTAR_FORMAT

   POSIX.1-1988 (ustar) 格式。

tarfile.GNU_FORMAT

   GNU tar 格式。

tarfile.PAX_FORMAT

   POSIX.1-2001 (pax) 格式。

tarfile.DEFAULT_FORMAT

   建立封存檔的預設格式。目前為 "PAX_FORMAT"。

   在 3.8 版的變更: 新封存檔的預設格式已從 "GNU_FORMAT" 變更為
   "PAX_FORMAT"。

也參考:

  "zipfile" 模組
     "zipfile" 標準模組的文件。

  Archiving operations
     標準 "shutil" 模組所提供的更高階封存功能的文件。

  GNU tar 手冊，基本 Tar 格式
     tar 封存檔案的文件，包括 GNU tar 擴充功能。


TarFile 物件
============

"TarFile" 物件提供一個 tar 封存檔的介面。tar 封存檔是一個區塊序列。一
個封存檔成員（一個儲存的檔案）由一個標頭區塊和後續的資料區塊組成。可以
在 tar 封存檔中多次儲存一個檔案。每個封存檔成員由一個 "TarInfo" 物件表
示，詳細資訊請參閱 TarInfo 物件。

"TarFile" 物件可以在 "with" 陳述式中用作情境管理器。當區塊完成時，它會
自動關閉。請注意，如果發生例外，以寫入方式開啟的封存檔將不會被最終化
(finalized)；只有內部使用的檔案物件會被關閉。使用案例請參閱 範例 一節
。

在 3.2 版被加入: 新增對情境管理協定的支援。

class tarfile.TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors='surrogateescape', pax_headers=None, debug=0, errorlevel=1, stream=False)

   以下所有引數都是可選的，也可以作為實例屬性存取。

   *name* 是封存檔的路徑名稱。*name* 可以是*類路徑物件*。如果提供了
   *fileobj* 則可以省略它。在這種情況下，如果檔案物件的 "name" 屬性存
   在，則會使用該屬性。

   *mode* 可以是 "'r'" 以從現有封存檔讀取、"'a'" 以附加資料到現有檔案
   、"'w'" 以建立新檔案覆蓋現有檔案，或 "'x'" 以僅在檔案不存在時建立新
   檔案。

   如果提供了 *fileobj*，它會被用於讀取或寫入資料。如果可以確定，
   *mode* 會被 *fileobj* 的模式覆蓋。*fileobj* 將從位置 0 開始使用。

   備註:

     當 "TarFile" 關閉時，*fileobj* 不會被關閉。

   *format* 控制寫入時的封存檔格式。它必須是在模組層級定義的常數
   "USTAR_FORMAT"、"GNU_FORMAT" 或 "PAX_FORMAT" 之一。讀取時，即使單一
   封存檔中存在不同格式，格式也會自動偵測。

   *tarinfo* 引數可用於將預設的 "TarInfo" 類別替換為不同的類別。

   如果 *dereference* 為 "False"，則將符號連結和硬連結新增到封存檔。如
   果為 "True"，則將目標檔案的內容新增到封存檔。這在不支援符號連結的系
   統上沒有效果。

   如果 *ignore_zeros* 為 "False"，則將空區塊視為封存檔的結尾。如果為
   "True"，則跳過空的（和無效的）區塊並嘗試取得盡可能多的成員。這僅對
   讀取串聯或損壞的封存檔有用。

   *debug* 可設定從 "0"（無偵錯訊息）到 "3"（所有偵錯訊息）。訊息會寫
   入 "sys.stderr"。

   *errorlevel* 控制如何處理解壓縮錯誤，請參閱"對應的屬性"。

   *encoding* 和 *errors* 引數定義用於讀取或寫入封存檔的字元編碼以及如
   何處理轉換錯誤。預設設定適用於大多數使用者。深入資訊請參閱 Unicode
   議題 一節。

   *pax_headers* 引數是一個可選的字串字典，如果 *format* 為
   "PAX_FORMAT"，則會作為 pax 全域標頭新增。

   如果 *stream* 設為 "True"，則讀取封存檔時不會快取封存檔中關於檔案的
   資訊，從而節省記憶體。

   在 3.2 版的變更: 使用 "'surrogateescape'" 作為 *errors* 引數的預設
   值。

   在 3.5 版的變更: 新增了 "'x'"（獨佔建立）模式。

   在 3.6 版的變更: *name* 參數接受*類路徑物件*。

   在 3.13 版的變更: 新增 *stream* 參數。

classmethod TarFile.open(...)

   替代建構函式。"tarfile.open()" 函式實際上是此類別方法的捷徑。

TarFile.getmember(name)

   回傳成員 *name* 的 "TarInfo" 物件。如果在封存檔中找不到 *name*，則
   引發 "KeyError"。

   備註:

     如果一個成員在封存檔中出現多次，則假定其最後一次出現是最新版本。

TarFile.getmembers()

   以 "TarInfo" 物件串列的形式回傳封存檔的成員。串列的順序與封存檔中成
   員的順序相同。

TarFile.getnames()

   以名稱串列的形式回傳成員。其順序與 "getmembers()" 回傳的串列順序相
   同。

TarFile.list(verbose=True, *, members=None)

   將內容表印出到 "sys.stdout"。如果 *verbose* 為 "False"，則只印出成
   員的名稱。如果為 "True"，則產生類似於 **ls -l** 的輸出。如果提供了
   可選的 *members*，它必須是 "getmembers()" 回傳串列的子集合。

   在 3.5 版的變更: 新增 *members* 參數。

TarFile.next()

   當 "TarFile" 以讀取方式開啟時，以 "TarInfo" 物件的形式回傳封存檔的
   下一個成員。如果沒有更多成員可用，則回傳 "None"。

TarFile.extractall(path='.', members=None, *, numeric_owner=False, filter=None)

   將所有成員從封存檔解壓縮到目前的工作目錄或目錄 *path*。如果提供了可
   選的 *members*，它必須是 "getmembers()" 回傳串列的子集合。目錄資訊
   （如擁有者、修改時間和權限）會在所有成員都被解壓縮後設定。這樣做是
   為了解決兩個問題：每次在目錄中建立檔案時，目錄的修改時間都會被重設
   。而且，如果目錄的權限不允許寫入，則無法將檔案解壓縮到其中。

   如果 *numeric_owner* 為 "True"，則使用 tar 檔案中的 uid 和 gid 數字
   來設定解壓縮檔案的擁有者／群組。否則，使用 tar 檔案中的名稱值。

   *filter* 引數指定在解壓縮之前如何修改或拒絕 "members"。詳細資訊請參
   閱 解壓縮篩選器。建議僅在需要特定 *tar* 功能時或作為
   "filter='data'" 以支援預設安全性較低的 Python 版本（3.13 及更低版本
   ）時明確設定此項。

   警告:

     切勿在未事先檢查的情況下解壓縮來自不受信任來源的封存檔。從 Python
     3.14 開始，預設值（"data"）將防止最危險的安全問題。但是，它不會防
     止*所有*意外或不安全的行為。詳細資訊請閱讀 解壓縮篩選器 一節。

   在 3.5 版的變更: 新增 *numeric_owner* 參數。

   在 3.6 版的變更: *path* 參數接受*類路徑物件*。

   在 3.12 版的變更: 新增 *filter* 參數。

   在 3.14 版的變更: *filter* 參數現在預設為 "'data'"。

TarFile.extract(member, path='', set_attrs=True, *, numeric_owner=False, filter=None)

   使用完整名稱將一個成員從封存檔解壓縮到目前的工作目錄。其檔案資訊會
   盡可能準確地解壓縮。*member* 可以是檔案名稱或 "TarInfo" 物件。你可
   以使用 *path* 指定不同的目錄。*path* 可以是*類路徑物件*。除非
   *set_attrs* 為 false，否則會設定檔案屬性 (owner, mtime, mode)。

   *numeric_owner* 和 *filter* 引數與 "extractall()" 相同。

   備註:

     "extract()" 方法不處理多個解壓縮問題。在大多數情況下，你應該考慮
     使用 "extractall()" 方法。

   警告:

     切勿在事先檢查之前從不受信任的來源解壓縮封存檔。詳細資訊請參閱
     "extractall()" 的警告。

   在 3.2 版的變更: 增加 *set_attrs* 參數。

   在 3.5 版的變更: 新增 *numeric_owner* 參數。

   在 3.6 版的變更: *path* 參數接受*類路徑物件*。

   在 3.12 版的變更: 新增 *filter* 參數。

TarFile.extractfile(member)

   以檔案物件的形式從封存檔解壓縮一個成員。*member* 可以是檔案名稱或
   "TarInfo" 物件。如果 *member* 是一般檔案或連結，則回傳
   "io.BufferedReader" 物件。對於所有其他現有成員，回傳 "None"。如果
   *member* 未出現在封存檔中，則引發 "KeyError"。

   在 3.3 版的變更: 回傳 "io.BufferedReader" 物件。

   在 3.13 版的變更: 回傳的 "io.BufferedReader" 物件具有 "mode" 屬性，
   該屬性始終等於 "'rb'"。

TarFile.errorlevel: int

   如果 *errorlevel* 為 "0"，則使用 "TarFile.extract()" 和
   "TarFile.extractall()" 時會忽略錯誤。然而，當 *debug* 大於 0 時，它
   們會在偵錯輸出中顯示為錯誤訊息。如果為 "1"（預設值），所有*致命*錯
   誤都會以 "OSError" 或 "FilterError" 例外的形式引發。如果為 "2"，所
   有*非致命*錯誤也會以 "TarError" 例外的形式引發。

   某些例外，例如由錯誤的引數型別或資料損壞引起的例外，總是會被引發。

   自訂的解壓縮篩選器應該對*致命*錯誤引發 "FilterError"，對*非致命*錯
   誤引發 "ExtractError"。

   請注意，當引發例外時，封存檔可能已部分解壓縮。清理是使用者的責任。

TarFile.extraction_filter

   在 3.12 版被加入.

   用作 "extract()" 和 "extractall()" 的 *filter* 引數預設的解壓縮篩選
   器。

   此屬性可以是 "None" 或可呼叫物件。與 "extract()" 的 *filter* 引數不
   同，此屬性不允許使用字串名稱。

   如果 "extraction_filter" 為 "None"（預設值），解壓縮方法將預設使用
   "data" 篩選器。

   此屬性可以在實例上設定或在子類別中覆寫。也可以在 "TarFile" 類別本身
   上設定它以設定全域預設值，儘管由於它會影響 *tarfile* 的所有使用，最
   佳做法是僅在頂層應用程式或 "site 組態"中這樣做。要以這種方式設定全
   域預設值，需要將篩選器函式包裝在 "staticmethod()" 中以防止注入
   "self" 引數。

   在 3.14 版的變更: 預設篩選器設為 "data"，這會禁止一些危險的功能，例
   如指向絕對路徑或目的地之外路徑的連結。在此之前，預設值等同於
   "fully_trusted"。

TarFile.add(name, arcname=None, recursive=True, *, filter=None)

   將檔案 *name* 新增到封存檔。*name* 可以是任何型別的檔案（目錄、FIFO
   、符號連結等）。如果提供了 *arcname*，則為封存檔中的檔案指定替代名
   稱。預設情況下，目錄會遞迴新增。這可以透過將 *recursive* 設為
   "False" 來避免。遞迴以排序順序新增條目。如果提供了 *filter*，它應該
   是一個接受 "TarInfo" 物件引數並回傳變更後的 "TarInfo" 物件的函式。
   如果它回傳 "None"，則 "TarInfo" 物件將從封存檔中排除。範例請參閱 範
   例。

   在 3.2 版的變更: 新增 *filter* 參數。

   在 3.7 版的變更: 遞迴以排序後的順序新增條目。

TarFile.addfile(tarinfo, fileobj=None)

   將 "TarInfo" 物件 *tarinfo* 新增到封存檔。如果 *tarinfo* 表示一個非
   零大小的一般檔案，*fileobj* 引數應該是*二進位檔案*，並從中讀取
   "tarinfo.size" 位元組並新增到封存檔。你可以直接建立 "TarInfo" 物件
   ，或使用 "gettarinfo()"。

   在 3.13 版的變更: 對於非零大小的一般檔案，必須提供 *fileobj*。

TarFile.gettarinfo(name=None, arcname=None, fileobj=None)

   從現有檔案的 "os.stat()" 或等效結果建立 "TarInfo" 物件。檔案可以由
   *name* 命名，或指定為具有檔案描述符的*檔案物件* *fileobj*。*name*
   可以是*類路徑物件*。如果提供了 *arcname*，則為封存檔中的檔案指定替
   代名稱，否則，名稱取自 *fileobj* 的 "name" 屬性或 *name* 引數。名稱
   應該是文字字串。

   在使用 "addfile()" 新增之前，你可以修改 "TarInfo" 的某些屬性。如果
   檔案物件不是位於檔案開頭的普通檔案物件，則可能需要修改諸如 "size"
   之類的屬性。這種情況適用於 "GzipFile" 等物件。"name" 也可以被修改，
   在這種情況下 *arcname* 可以是一個虛設字串 (dummy string)。

   在 3.6 版的變更: *name* 參數接受*類路徑物件*。

TarFile.close()

   關閉 "TarFile"。在寫入模式下，會在封存檔末尾附加兩個結束零區塊。

TarFile.pax_headers: dict

   包含 pax 全域標頭鍵值對的字典。


TarInfo 物件
============

"TarInfo" 物件表示 "TarFile" 中的一個成員。除了儲存檔案的所有必要屬性
（如檔案型別、大小、時間、權限、擁有者等）之外，它還提供一些有用的方法
來確定其型別。它*不*包含檔案的資料本身。

"TarInfo" 物件由 "TarFile" 的方法 "getmember()"、"getmembers()" 和
"gettarinfo()" 回傳。

修改由 "getmember()" 或 "getmembers()" 回傳的物件將影響封存檔上的所有
後續操作。對於不希望出現這種情況的情況，你可以使用 "copy.copy()" 或呼
叫 "replace()" 方法來一步建立修改後的副本。

可以將多個屬性設為 "None" 以表示一段中介資料 (metadata) 未被使用或未知
。不同的 "TarInfo" 方法會以不同的方式處理 "None"：

* "extract()" 或 "extractall()" 方法將忽略對應的中介資料，使其保持預設
  值。

* "addfile()" 將失敗。

* "list()" 將印出一個佔位字串 (placeholder string)。

class tarfile.TarInfo(name='')

   建立 "TarInfo" 物件。

classmethod TarInfo.frombuf(buf, encoding, errors)

   從字串緩衝區 *buf* 建立並回傳 "TarInfo" 物件。

   如果緩衝區無效，則引發 "HeaderError"。

classmethod TarInfo.fromtarfile(tarfile)

   從 "TarFile" 物件 *tarfile* 讀取下一個成員並將其作為 "TarInfo" 物件
   回傳。

TarInfo.tobuf(format=DEFAULT_FORMAT, encoding=ENCODING, errors='surrogateescape')

   從 "TarInfo" 物件建立字串緩衝區。關於引數的資訊，請參閱 "TarFile"
   類別的建構函式。

   在 3.2 版的變更: 使用 "'surrogateescape'" 作為 *errors* 引數的預設
   值。

一個 "TarInfo" 物件具有以下公開資料屬性：

TarInfo.name: str

   封存檔成員的名稱。

TarInfo.size: int

   大小（以位元組為單位）。

TarInfo.mtime: int | float

   自epoch 以來的最後修改時間（以秒為單位），如
   "os.stat_result.st_mtime"。

   在 3.12 版的變更: 對於 "extract()" 和 "extractall()"，可以設為
   "None"，讓解壓縮跳過套用此屬性。

TarInfo.mode: int

   權限位元 (permission bits)，如 "os.chmod()" 所用。

   在 3.12 版的變更: 對於 "extract()" 和 "extractall()"，可以設為
   "None"，讓解壓縮跳過套用此屬性。

TarInfo.type

   檔案類型。*type* 通常是這些常數之一："REGTYPE"、"AREGTYPE"、
   "LNKTYPE"、"SYMTYPE"、"DIRTYPE"、"FIFOTYPE"、"CONTTYPE"、"CHRTYPE"
   、"BLKTYPE"、"GNUTYPE_SPARSE"。要更方便地確定 "TarInfo" 物件的類型
   ，請使用下面的 "is*()" 方法。

TarInfo.linkname: str

   目標檔案名稱的名稱，僅存在於型別為 "LNKTYPE" 和 "SYMTYPE" 的
   "TarInfo" 物件中。

   對於符號連結（"SYMTYPE"），*linkname* 相對於包含連結的目錄。對於硬
   連結（"LNKTYPE"），*linkname* 相對於封存檔的根。

TarInfo.uid: int

   最初儲存此成員的使用者的使用者 ID。

   在 3.12 版的變更: 對於 "extract()" 和 "extractall()"，可以設為
   "None"，讓解壓縮跳過套用此屬性。

TarInfo.gid: int

   最初儲存此成員的使用者的群組 ID。

   在 3.12 版的變更: 對於 "extract()" 和 "extractall()"，可以設為
   "None"，讓解壓縮跳過套用此屬性。

TarInfo.uname: str

   使用者名稱。

   在 3.12 版的變更: 對於 "extract()" 和 "extractall()"，可以設為
   "None"，讓解壓縮跳過套用此屬性。

TarInfo.gname: str

   群組名稱。

   在 3.12 版的變更: 對於 "extract()" 和 "extractall()"，可以設為
   "None"，讓解壓縮跳過套用此屬性。

TarInfo.chksum: int

   標頭校驗和 (checksum)。

TarInfo.devmajor: int

   裝置主號碼。

TarInfo.devminor: int

   裝置副號碼。

TarInfo.offset: int

   tar 標頭從這裡開始。

TarInfo.offset_data: int

   檔案的資料從這裡開始。

TarInfo.sparse

   稀疏成員資訊。

TarInfo.pax_headers: dict

   包含關聯的 pax 擴充標頭鍵值對的字典。

TarInfo.replace(name=..., mtime=..., mode=..., linkname=..., uid=..., gid=..., uname=..., gname=..., deep=True)

   在 3.12 版被加入.

   回傳具有給定屬性變更的 "TarInfo" 物件的*新*副本。例如，要回傳群組名
   稱設為 "'staff'" 的 "TarInfo"，請使用：

      new_tarinfo = old_tarinfo.replace(gname='staff')

   預設情況下，會進行深度複製。如果 *deep* 為 false，則複製為淺複製，
   即 "pax_headers" 和任何自訂屬性與原始 "TarInfo" 物件共用。

"TarInfo" 物件也提供一些方便的查詢方法：

TarInfo.isfile()

   如果 "TarInfo" 物件是一般檔案，則回傳 "True"。

TarInfo.isreg()

   與 "isfile()" 相同。

TarInfo.isdir()

   如果它是一個目錄，則回傳 "True"。

TarInfo.issym()

   如果它是一個符號連結，則回傳 "True"。

TarInfo.islnk()

   如果它是一個硬連結，則回傳 "True"。

TarInfo.ischr()

   如果它是一個字元裝置，則回傳 "True"。

TarInfo.isblk()

   如果它是一個區塊裝置，則回傳 "True"。

TarInfo.isfifo()

   如果它是一個 FIFO，則回傳 "True"。

TarInfo.isdev()

   如果它是一個字元裝置、區塊裝置或 FIFO，則回傳 "True"。


解壓縮篩選器
============

在 3.12 版被加入.

*tar* 格式是為了捕捉 UNIX-like 檔案系統的所有細節而設計的，這使它非常
強大。不幸的是，這些功能使得建立在解壓縮時具有非預期甚至可能是惡意影響
的 tar 檔案變得很容易。例如，解壓縮 tar 檔案可以透過各種方式覆寫任意檔
案（例如使用絕對路徑、".." 路徑元件，或影響後續成員的符號連結）。

在大多數情況下，不需要完整功能。因此，*tarfile* 支援解壓縮篩選器：一種
限制功能的機制，從而減輕某些安全性問題。

警告:

  沒有任何可用的篩選器會阻擋*所有*危險的封存檔功能。永遠不要在沒有事先
  檢查的情況下從不受信任的來源解壓縮封存檔。另請參閱 進一步驗證的提示
  。

也參考:

  **PEP 706**
     包含此設計背後的進一步動機和理由。

"TarFile.extract()" 或 "extractall()" 的 *filter* 引數可以是：

* "'fully_trusted'" 字串：遵守封存檔中指定的所有中介資料。應在使用者完
  全信任封存檔或實作自己的複雜驗證時使用。

* "'tar'" 字串：遵守大多數 *tar* 特定功能（即 UNIX-like 檔案系統的功能
  ），但阻擋很可能令人驚訝或惡意的功能。詳見 "tar_filter()"。

* "'data'" 字串：忽略或阻擋大多數 UNIX-like 檔案系統特定功能。用於解壓
  縮跨平台資料封存檔。詳見 "data_filter()"。

* "None"（預設）：使用 "TarFile.extraction_filter"。

  如果該值也是 "None"（預設值），則會使用 "'data'" 篩選器。

     在 3.14 版的變更: 預設篩選器設為 "data"。在此之前，預設等同於
     "fully_trusted"。

* 一個可呼叫物件，對於每個解壓縮的成員，會使用描述該成員的 TarInfo 和
  封存檔解壓縮目的地路徑來呼叫它（即所有成員使用相同的路徑）：

     filter(member: TarInfo, path: str, /) -> TarInfo | None

  此可呼叫物件在每個成員解壓縮之前被呼叫，因此可以考慮磁碟的目前狀態。
  它可以：

  * 回傳一個 "TarInfo" 物件，該物件將用於替代封存檔中的中介資料，或

  * 回傳 "None"，在此情況下該成員會被跳過，或

  * 引發例外以中止操作或跳過該成員，取決於 "errorlevel"。請注意，當解
    壓縮被中止時，"extractall()" 可能會留下部分解壓縮的封存檔。它不會
    嘗試清理。


預設具名篩選器
--------------

預先定義的具名篩選器可作為函式使用，因此可以在自訂篩選器中重複使用：

tarfile.fully_trusted_filter(member, path)

   回傳未經修改的 *member*。

   這實作了 "'fully_trusted'" 篩選器。

tarfile.tar_filter(member, path)

   實作 "'tar'" 篩選器。

   * 從檔名中移除開頭的斜線（"/" 和 "os.sep"）。

   * 拒絕解壓縮具有絕對路徑的檔案（在移除斜線後名稱仍是絕對路徑的情況
     下，例如 Windows 上的 "C:/foo"）。這會引發 "AbsolutePathError"。

   * 拒絕解壓縮其絕對路徑（在跟隨符號連結後）會位於目的地之外的檔案。
     這會引發 "OutsideDestinationError"。

   * 清除高位模式位元 (setuid, setgid, sticky) 以及群組/其他人的寫入位
     元（"S_IWGRP" | "S_IWOTH"）。

   回傳修改後的 "TarInfo" 成員。

tarfile.data_filter(member, path)

   實作 "'data'" 篩選器。除了 "tar_filter" 的功能外，還包括：

   * 使用 "os.path.normpath()" 正規化連結目標（"TarInfo.linkname"）。
     請注意，這會移除內部的 ".." 元件，如果 "TarInfo.linkname" 中的路
     徑會遍歷符號連結，這可能會改變連結的意義。

   * 拒絕解壓縮指向絕對路徑的連結（硬連結或軟連結），或指向目的地之外
     的連結。

     這會引發 "AbsoluteLinkError" 或 "LinkOutsideDestinationError"。

     請注意，即使在不支援符號連結的平台上，這類檔案也會被拒絕。

   * 拒絕解壓縮裝置檔案（包括 pipe）。這會引發 "SpecialFileError"。

   * 對於一般檔案，包括硬連結：

     * 設定擁有者的讀取和寫入權限（"S_IRUSR" | "S_IWUSR"）。

     * 如果擁有者沒有執行權限（"S_IXUSR"），則移除群組和其他人的執行權
       限（"S_IXGRP" | "S_IXOTH"）。

   * 對於其他檔案（目錄），將 "mode" 設為 "None"，讓解壓縮方法跳過套用
     權限位元。

   * 將使用者和群組資訊 ("uid", "gid", "uname", "gname") 設為 "None"，
     讓解壓縮方法跳過設定它們。

   回傳修改後的 "TarInfo" 成員。

   請注意，此篩選器並未阻擋*所有*危險的封存檔功能。詳細資訊請參閱 進一
   步驗證的提示。

   在 3.14 版的變更: 連結目標現在會被正規化。


篩選器錯誤
----------

當篩選器拒絕解壓縮檔案時，它會引發適當的例外，該例外是 "FilterError"
的子類別。如果 "TarFile.errorlevel" 為 1 或更高，這將中止解壓縮。使用
"errorlevel=0" 時，錯誤將被記錄並跳過該成員，但解壓縮將繼續進行。


進一步驗證的提示
----------------

即使使用 "filter='data'"，*tarfile* 也不適合在沒有事先檢查的情況下解壓
縮不受信任的檔案。除了其他問題外，預先定義的篩選器不會防止阻斷服務攻擊
。使用者應進行額外檢查。

以下是需要考慮的不完整清單：

* 解壓縮到"新的臨時目錄"以防止例如利用預先存在的連結，並使解壓縮失敗後
  更容易清理。

* 如果不需要符號連結功能，請禁止它。

* 處理不受信任的資料時，請使用外部（例如作業系統層級）的磁碟、記憶體和
  CPU 使用量限制。

* 根據字元白名單檢查檔名（以過濾掉控制字元、易混淆字元、外部路徑分隔符
  號等）。

* 檢查檔名是否具有預期的副檔名（避免點擊時會執行的檔案，或像 Windows
  特殊裝置名稱這樣的無副檔名檔案）。

* 限制解壓縮檔案的數量、解壓縮資料的總大小、檔名長度（包括符號連結長度
  ）以及個別檔案的大小。

* 檢查在不區分大小寫的檔案系統上會被遮蔽的檔案。

另請注意：

* Tar 檔案可能包含同一檔案的多個版本。較晚的版本預期會覆蓋較早的版本。
  此功能對於允許更新磁帶封存檔至關重要，但可能被惡意濫用。

* *tarfile* 不會防止「即時」資料的問題，例如攻擊者在解壓縮（或封存）進
  行中時篡改目的地（或來源）目錄。


支援舊版 Python
---------------

解壓縮篩選器在 Python 3.12 中加入，但可能會作為安全性更新移植到較舊版
本。要檢查此功能是否可用，請使用 "hasattr(tarfile, 'data_filter')" 而
不是檢查 Python 版本。

以下範例顯示如何支援有此功能和沒有此功能的 Python 版本。請注意，設定
"extraction_filter" 會影響任何後續操作。

* 完全信任的封存檔：

     my_tarfile.extraction_filter = (lambda member, path: member)
     my_tarfile.extractall()

* 如果可用則使用 "'data'" 篩選器，但如果此功能不可用則使用後備的
  Python 3.11 的行為（"'fully_trusted'"）：

     my_tarfile.extraction_filter = getattr(tarfile, 'data_filter',
                                            (lambda member, path: member))
     my_tarfile.extractall()

* 使用 "'data'" 篩選器；如果不可用則*失敗*：

     my_tarfile.extractall(filter=tarfile.data_filter)

  或：

     my_tarfile.extraction_filter = tarfile.data_filter
     my_tarfile.extractall()

* 使用 "'data'" 篩選器；如果不可用則*警告*：

     if hasattr(tarfile, 'data_filter'):
         my_tarfile.extractall(filter='data')
     else:
         # 當不再需要時移除它
         warn_the_user('Extracting may be unsafe; consider updating Python')
         my_tarfile.extractall()


有狀態的解壓縮篩選器範例
------------------------

雖然 *tarfile* 的解壓縮方法接受一個簡單的 *filter* 可呼叫物件，但自訂
篩選器可能是具有內部狀態的更複雜物件。將這些寫成情境管理器可能很有用，
像這樣使用：

   with StatefulFilter() as filter_func:
       tar.extractall(path, filter=filter_func)

例如，這樣的篩選器可以寫成：

   class StatefulFilter:
       def __init__(self):
           self.file_count = 0

       def __enter__(self):
           return self

       def __call__(self, member, path):
           self.file_count += 1
           return member

       def __exit__(self, *exc_info):
           print(f'{self.file_count} files extracted')


命令列介面
==========

在 3.4 版被加入.

"tarfile" 模組提供了簡單的命令列介面來與 tar 封存檔互動。

如果你要建立新的 tar 封存檔，請在 "-c" 選項後指定其名稱，然後列出應包
含的檔案名稱：

   $ python -m tarfile -c monty.tar  spam.txt eggs.txt

也可以傳遞目錄：

   $ python -m tarfile -c monty.tar life-of-brian_1979/

如果你要將 tar 封存檔解壓縮到目前目錄，請使用 "-e" 選項：

   $ python -m tarfile -e monty.tar

你也可以透過傳遞目錄名稱，將 tar 封存檔解壓縮到不同的目錄：

   $ python -m tarfile -e monty.tar  other-dir/

若要列出 tar 封存檔中的檔案，請使用 "-l" 選項：

   $ python -m tarfile -l monty.tar


命令列選項
----------

-l <tarfile>
--list <tarfile>

   列出 tarfile 中的檔案。

-c <tarfile> <source1> ... <sourceN>
--create <tarfile> <source1> ... <sourceN>

   從來源檔案建立 tarfile。

-e <tarfile> [<output_dir>]
--extract <tarfile> [<output_dir>]

   如果未指定 *output_dir*，則將 tarfile 解壓縮到目前目錄。

-t <tarfile>
--test <tarfile>

   測試 tarfile 是否有效。

-v, --verbose

   詳細輸出。

--filter <filtername>

   為 "--extract" 指定 *filter*。詳見 解壓縮篩選器。僅接受字串名稱（即
   "fully_trusted"、"tar" 和 "data"）。


範例
====


讀取範例
--------

如何將整個 tar 封存檔解壓縮到目前的工作目錄：

   import tarfile
   tar = tarfile.open("sample.tar.gz")
   tar.extractall(filter='data')
   tar.close()

如何使用產生器函式而非串列，來以 "TarFile.extractall()" 解壓縮 tar 封
存檔的子集：

   import os
   import tarfile

   def py_files(members):
       for tarinfo in members:
           if os.path.splitext(tarinfo.name)[1] == ".py":
               yield tarinfo

   tar = tarfile.open("sample.tar.gz")
   tar.extractall(members=py_files(tar))
   tar.close()

如何讀取 gzip 壓縮的 tar 封存檔並顯示部分成員資訊：

   import tarfile
   tar = tarfile.open("sample.tar.gz", "r:gz")
   for tarinfo in tar:
       print(tarinfo.name, "is", tarinfo.size, "bytes in size and is ", end="")
       if tarinfo.isreg():
           print("a regular file.")
       elif tarinfo.isdir():
           print("a directory.")
       else:
           print("something else.")
   tar.close()


寫入範例
--------

如何從檔案名稱串列建立未壓縮的 tar 封存檔：

   import tarfile
   tar = tarfile.open("sample.tar", "w")
   for name in ["foo", "bar", "quux"]:
       tar.add(name)
   tar.close()

使用 "with" 陳述式的相同範例：

   import tarfile
   with tarfile.open("sample.tar", "w") as tar:
       for name in ["foo", "bar", "quux"]:
           tar.add(name)

如何在 "TarFile.add()" 的 *fileobj* 參數中使用 "sys.stdout.buffer" 建
立封存檔並寫入標準輸出：

   import sys
   import tarfile
   with tarfile.open("sample.tar.gz", "w|gz", fileobj=sys.stdout.buffer) as tar:
       for name in ["foo", "bar", "quux"]:
           tar.add(name)

如何在 "TarFile.add()" 中使用 *filter* 參數來建立封存檔並重設使用者資
訊：

   import tarfile
   def reset(tarinfo):
       tarinfo.uid = tarinfo.gid = 0
       tarinfo.uname = tarinfo.gname = "root"
       return tarinfo
   tar = tarfile.open("sample.tar.gz", "w:gz")
   tar.add("foo", filter=reset)
   tar.close()


支援的 tar 格式
===============

使用 "tarfile" 模組可以建立三種 tar 格式：

* POSIX.1-1988 ustar 格式（"USTAR_FORMAT"）。它支援最多 256 個字元的檔
  名和最多 100 個字元的連結名稱。檔案大小上限為 8 GiB。這是一種老舊且
  受限的格式，但廣受支援。

* GNU tar 格式（"GNU_FORMAT"）。它支援長檔名和長連結名稱、超過 8 GiB
  的檔案以及稀疏檔案。這是 GNU/Linux 系統上的實際標準。"tarfile" 完全
  支援 GNU tar 對長名稱的擴充，但稀疏檔案僅有唯讀支援。

* POSIX.1-2001 pax 格式（"PAX_FORMAT"）。這是最靈活的格式，幾乎沒有任
  何限制。它支援長檔名和長連結名稱、大型檔案，並以可攜的方式儲存路徑名
  稱。現代的 tar 實作（包括 GNU tar、bsdtar/libarchive 和 star）都完全
  支援擴充的 *pax* 功能；某些舊的或未維護的函式庫可能不支援，但應將
  *pax* 封存檔視為普遍支援的 *ustar* 格式。這是目前建立新封存檔的預設
  格式。

  它透過額外的標頭擴充既有的 *ustar* 格式，以儲存無法以其他方式儲存的
  資訊。pax 標頭有兩種類型：擴充標頭（Extended headers）只影響緊接在後
  的檔案標頭，全域標頭（global headers）則對整個封存檔有效並影響所有後
  續檔案。為了可攜性，pax 標頭中的所有資料都使用 *UTF-8* 編碼。

還有一些 tar 格式的變體可以讀取，但無法建立：

* 古老的 V7 格式。這是 Unix 第七版的第一個 tar 格式，只能儲存一般檔案
  和目錄。名稱不得超過 100 個字元，且沒有使用者/群組名稱資訊。某些封存
  檔在欄位包含非 ASCII 字元時，標頭檢查碼會計算錯誤。

* SunOS tar 擴充格式。此格式是 POSIX.1-2001 pax 格式的變體，但並不相容
  。


Unicode 議題
============

tar 格式最初是為了在磁帶機上進行備份而設計的，主要著重於保存檔案系統資
訊。現今 tar 封存檔常用於檔案發布和透過網路交換封存檔。原始格式（也是
所有其他格式的基礎）的一個問題是，它沒有支援不同字元編碼的概念。例如，
在 *UTF-8* 系統上建立的普通 tar 封存檔，如果包含非 *ASCII* 字元，在
*Latin-1* 系統上就無法正確讀取。文字中介資料（如檔名、連結名稱、使用者
/群組名稱）會顯示為損壞。不幸的是，無法自動偵測封存檔的編碼。pax 格式
就是為了解決此問題而設計的。它使用通用字元編碼 *UTF-8* 來儲存非 ASCII
中介資料。

"tarfile" 中字元轉換的細節由 "TarFile" 類別的 *encoding* 和 *errors*
關鍵字引數控制。

*encoding* 定義封存檔中介資料所使用的字元編碼。預設值為
"sys.getfilesystemencoding()"，若無法取得則使用 "'ascii'" 作為備用。根
據封存檔是讀取還是寫入，中介資料必須被解碼或編碼。如果 *encoding* 設定
不當，此轉換可能會失敗。

*errors* 引數定義如何處理無法轉換的字元。可能的值列於 Error Handlers
一節。預設方案是 "'surrogateescape'"，這也是 Python 用於檔案系統呼叫的
方案，參見 File Names, Command Line Arguments, and Environment
Variables。

對於 "PAX_FORMAT" 封存檔（預設格式），通常不需要 *encoding*，因為所有
中介資料都使用 *UTF-8* 儲存。*encoding* 只在少數情況下使用，例如解碼二
進位 pax 標頭或儲存包含代理字元的字串時。
