"zipfile" --- 處理 ZIP 封存檔案
*******************************

**原始碼：**Lib/zipfile/

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

ZIP 檔案格式是一種常見的封存 (archive) 與壓縮標準。本模組提供了建立、
讀取、寫入、附加與列出 ZIP 檔案的工具。任何對本模組的進階使用都將需要
對 PKZIP Application Note 中定義的格式有所理解。

本模組不處理多組件 (multipart) ZIP 檔案。它可以處理使用 ZIP64 擴充（即
大於 4 GiB 的 ZIP 檔案）的 ZIP 檔案。它支援解密 ZIP 封存檔案中的加密檔
案，但目前無法建立加密檔案。解密速度極慢，因為它是在原生 Python 中實作
，而不是 C。

Handling compressed archives requires *optional modules* such as
"zlib", "bz2", "lzma", and "compression.zstd". If any of them are
missing from your copy of CPython, look for documentation from your
distributor (that is, whoever provided Python to you). If you are the
distributor, see 可選模組的需求.

本模組定義了以下項目：

exception zipfile.BadZipFile

   對於損壞的 ZIP 檔案所引發的錯誤。

   在 3.2 版被加入.

exception zipfile.BadZipfile

   "BadZipFile" 的別名，為了與舊版 Python 相容。

   在 3.2 版之後被棄用.

exception zipfile.LargeZipFile

   當 ZIP 檔案需要 ZIP64 功能但該功能未被啟用時所引發的錯誤。

class zipfile.ZipFile

   用於讀取和寫入 ZIP 檔案的類別。有關建構函式的詳細資訊，請參閱
   ZipFile 物件 章節。

class zipfile.Path

   實作了 "pathlib.Path" 所提供介面子集的類別，包括完整的
   "importlib.resources.abc.Traversable" 介面。

   在 3.8 版被加入.

class zipfile.PyZipFile

   用於建立包含 Python 函式庫的 ZIP 封存檔案的類別。

class zipfile.ZipInfo(filename='NoName', date_time=(1980, 1, 1, 0, 0, 0))

   用於表示封存檔案中成員資訊的類別。此類別的實例由 "ZipFile" 物件的
   "getinfo()" 和 "infolist()" 方法回傳。大多數 "zipfile" 模組的使用者
   不需要建立這些實例，而只需使用本模組所建立的。*filename* 應為封存成
   員的完整名稱，而 *date_time* 應為一個包含六個欄位的元組，用以描述檔
   案的最後修改時間；這些欄位在 ZipInfo 物件 章節中有所描述。

   在 3.13 版的變更: 新增了一個公開的 "compress_level" 屬性，以公開先
   前受保護的 "_compresslevel"。為了向後相容，舊的受保護名稱仍可作為一
   個特性繼續運作。

   _for_archive(archive)

      Resolve the date_time, compression attributes, and external
      attributes to suitable defaults as used by "ZipFile.writestr()".

      Returns self for chaining.

      在 3.14 版被加入.

zipfile.is_zipfile(filename)

   如果 *filename* 根據其魔術數字（magic number）是一個有效的 ZIP 檔案
   ，則回傳 "True"，否則回傳 "False"。*filename* 也可以是一個檔案或類
   檔案物件。

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

zipfile.ZIP_STORED

   用於未壓縮封存成員的數值常數。

zipfile.ZIP_DEFLATED

   用於常用的 ZIP 壓縮方法的數值常數。這需要 "zlib" 模組。

zipfile.ZIP_BZIP2

   用於 BZIP2 壓縮方法的數值常數。這需要 "bz2" 模組。

   在 3.3 版被加入.

zipfile.ZIP_LZMA

   用於 LZMA 壓縮方法的數值常數。這需要 "lzma" 模組。

   在 3.3 版被加入.

zipfile.ZIP_ZSTANDARD

   用於 Zstandard 壓縮方法的數值常數。這需要 "compression.zstd" 模組。

   備註:

     In APPNOTE 6.3.7, the method ID "20" was assigned to Zstandard
     compression. This was changed in APPNOTE 6.3.8 to method ID "93"
     to avoid conflicts, with method ID "20" being deprecated. For
     compatibility, the "zipfile" module reads both method IDs but
     will only write data with method ID "93".

   在 3.14 版被加入.

備註:

  ZIP 檔案格式規範自 2001 年起已包含對 bzip2 壓縮的支援、自 2006 年起
  支援 LZMA 壓縮、自 2020 年起支援 Zstandard 壓縮。然而某些工具（包括
  舊版的 Python）不支援這些壓縮方法，可能會完全拒絕處理該 ZIP 檔案，或
  無法解壓縮個別檔案。

也參考:

  PKZIP Application Note
     由 Phil Katz（該格式與所用演算法的創造者）所撰寫的關於 ZIP 檔案格
     式的文件。

  Info-ZIP 首頁
     關於 Info-ZIP 專案的 ZIP 封存程式和開發函式庫的資訊。


ZipFile 物件
============

class zipfile.ZipFile(file, mode='r', compression=ZIP_STORED, allowZip64=True, compresslevel=None, *, strict_timestamps=True, metadata_encoding=None)

   開啟一個 ZIP 檔案，其中 *file* 可以是一個檔案的路徑（一個字串）、一
   個類檔案物件或一個 *path-like object*。

   *mode* 參數應為 "'r'" 來讀取一個現有檔案、"'w'" 來清空並寫入一個新
   檔案、"'a'" 來附加到一個現有檔案，或 "'x'" 來獨佔性地建立並寫入一個
   新檔案。如果 *mode* 為 "'x'" 且 *file* 指向一個現有檔案，將會引發
   "FileExistsError"。如果 *mode* 為 "'a'" 且 *file* 指向一個現有的
   ZIP 檔案，則會將額外的檔案加入其中。如果 *file* 並非指向一個 ZIP 檔
   案，則一個新的 ZIP 封存檔案會被附加到該檔案之後。這適用於將一個 ZIP
   封存檔案附加到另一個檔案（例如 "python.exe"）。如果 *mode* 為 "'a'"
   且檔案完全不存在，它會被建立。如果 *mode* 為 "'r'" 或 "'a'"，該檔案
   應為可定位的 (seekable)。

   *compression* 是寫入封存檔案時要使用的 ZIP 壓縮方法，應為
   "ZIP_STORED"、"ZIP_DEFLATED"、"ZIP_BZIP2"、"ZIP_LZMA" 或
   "ZIP_ZSTANDARD"；無法識別的值將導致引發 "NotImplementedError"。如果
   指定了 "ZIP_DEFLATED"、"ZIP_BZIP2"、"ZIP_LZMA" 或 "ZIP_ZSTANDARD"，
   但對應的模組（"zlib"、"bz2"、"lzma" 或 "compression.zstd"）不可用，
   則會引發 "RuntimeError"。預設為 "ZIP_STORED"。

   如果 *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"）。當使用
   "ZIP_ZSTANDARD" 時，通常接受整數 "-131072" 到 "22"（更多關於檢索有
   效值及其含義，請參閱 "CompressionParameter.compression_level"）。

   *strict_timestamps* 引數，當設為 "False" 時，允許壓縮早於
   1980-01-01 的檔案，代價是將時間戳記設為 1980-01-01。對於晚於
   2107-12-31 的檔案，也會有類似的行為，時間戳記同樣會被設為極限值。

   當 mode 為 "'r'" 時，*metadata_encoding* 可以設為一個編解碼器的名稱
   ，該編解碼器將被用來解碼元資料，例如成員名稱和 ZIP 註解。

   如果檔案以 "'w'"、"'x'" 或 "'a'" 模式建立，然後在沒有向封存檔案中加
   入任何檔案的情況下 "關閉"，則適用於空封存檔案的 ZIP 結構將被寫入檔
   案中。

   ZipFile 也是一個情境管理器，因此支援 "with" 陳述式。在範例中，
   *myzip* 在 "with" 陳述式的程式碼區塊執行完畢後會被關閉 --- 即使發生
   例外也是如此：

      with ZipFile('spam.zip', 'w') as myzip:
          myzip.write('eggs.txt')

   備註:

     *metadata_encoding* 是 ZipFile 的一個實例範圍設定。無法以每個成員
     為基礎來設定此項。此屬性是針對舊版實作的變通方法，這些實作會以目
     前區域編碼或頁碼（主要在 Windows 上）產生帶有名稱的封存檔案。根據
     .ZIP 標準，元資料的編碼可以透過封存檔案標頭中的一個旗標指定為 IBM
     頁碼（預設）或 UTF-8。該旗標的優先級高於 *metadata_encoding*，後
     者是 Python 特有的擴充。

   在 3.2 版的變更: 新增 "ZipFile" 作為情境管理器使用的能力。

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

   在 3.4 版的變更: ZIP64 擴充預設為啟用。

   在 3.5 版的變更: 新增了對寫入不可定位的 (unseekable) 串流的支援。新
   增了對 "'x'" 模式的支援。

   在 3.6 版的變更: 先前，對於無法識別的壓縮值，會引發一個單純的
   "RuntimeError"。

   在 3.6.2 版的變更: *file* 參數接受一個 *path-like object*。

   在 3.7 版的變更: 新增 *compresslevel* 參數。

   在 3.8 版的變更: *strict_timestamps* 僅限關鍵字參數。

   在 3.11 版的變更: 新增了在讀取 zipfile 目錄和檔案標頭中的元資料時，
   可指定成員名稱編碼的支援。

ZipFile.close()

   關閉封存檔案。你必須在結束程式前呼叫 "close()"，否則必要的記錄將不
   會被寫入。

ZipFile.getinfo(name)

   回傳一個帶有關於封存成員 *name* 資訊的 "ZipInfo" 物件。對一個目前不
   包含在封存檔案中的名稱呼叫 "getinfo()" 將會引發 "KeyError"。

ZipFile.infolist()

   回傳一個串列，其中為每個封存成員包含一個 "ZipInfo" 物件。如果開啟的
   是一個現有的封存檔案，這些物件的順序與它們在磁碟上實際 ZIP 檔案中的
   條目順序相同。

ZipFile.namelist()

   依名稱回傳封存成員的串列。

ZipFile.open(name, mode='r', pwd=None, *, force_zip64=False)

   以二進位類檔案物件的形式存取封存檔案中的一個成員。*name* 可以是封存
   檔案內的檔案名稱，或是一個 "ZipInfo" 物件。*mode* 參數如果包含，則
   必須是 "'r'"（預設值）或 "'w'"。*pwd* 是用於解密加密 ZIP 檔案的密碼
   ，為一個 "bytes" 物件。

   "open()" 也是一個情境管理器，因此支援 "with" 陳述式：

      with ZipFile('spam.zip') as myzip:
          with myzip.open('eggs.txt') as myfile:
              print(myfile.read())

   在 *mode* 為 "'r'" 時，該類檔案物件（"ZipExtFile"）是唯讀的，並提供
   以下方法："read()"、"readline()"、"readlines()"、"seek()"、"tell()"
   、"__iter__()"、"__next__()"。這些物件可以獨立於 ZipFile 運作。

   在 "mode='w'" 時，會回傳一個可寫的檔案控點 (file handle)，它支援
   "write()" 方法。當一個可寫的檔案控點開啟時，嘗試讀取或寫入 ZIP 檔案
   中的其他檔案將會引發 "ValueError"。

   在這兩種情況下，該類檔案物件也具有 "name" 屬性，它相當於封存檔案內
   的檔案名稱，以及 "mode" 屬性，其值為 "'rb'" 或 "'wb'"，取決於輸入的
   模式。

   寫入檔案時，如果檔案大小預先未知但可能超過 2 GiB，請傳入
   "force_zip64=True" 以確保標頭格式能夠支援大檔案。如果檔案大小預先已
   知，請建構一個設定了 "file_size" 的 "ZipInfo" 物件，並將其用作
   *name* 參數。

   備註:

     "open()"、"read()" 和 "extract()" 方法可以接受一個檔名或一個
     "ZipInfo" 物件。當你嘗試讀取一個包含重複名稱成員的 ZIP 檔案時，你
     會發現這很有用。

   在 3.6 版的變更: 移除了對 "mode='U'" 的支援。請使用
   "io.TextIOWrapper" 以 *universal newlines* 模式讀取壓縮的文字檔。

   在 3.6 版的變更: "ZipFile.open()" 現在可以用於以 "mode='w'" 選項將
   檔案寫入封存檔案。

   在 3.6 版的變更: 在一個已關閉的 ZipFile 上呼叫 "open()" 將會引發
   "ValueError"。先前，會引發一個 "RuntimeError"。

   在 3.13 版的變更: 為可寫的類檔案物件新增了 "name" 和 "mode" 屬性。
   可讀的類檔案物件的 "mode" 屬性值從 "'r'" 變更為 "'rb'"。

ZipFile.extract(member, path=None, pwd=None)

   從封存檔案中解壓縮一個成員到目前工作目錄；*member* 必須是其完整名稱
   或一個 "ZipInfo" 物件。其檔案資訊會被盡可能精確地解壓縮。*path* 指
   定一個不同的目錄來解壓縮。*member* 可以是一個檔名或一個 "ZipInfo"
   物件。*pwd* 是用於加密檔案的密碼，為一個 "bytes" 物件。

   回傳建立的正規化路徑（一個目錄或新檔案）。

   備註:

     如果成員檔名是絕對路徑，磁碟機/UNC 共享點和開頭的（反）斜線將被移
     除，例如：在 Unix 上 "///foo/bar" 變成 "foo/bar"，在 Windows 上
     "C:\foo\bar" 變成 "foo\bar"。並且成員檔名中所有的 "".."" 元件將被
     移除，例如："../../foo../../ba..r" 變成 "foo../ba..r"。在 Windows
     上，非法字元（":"、"<"、">"、"|"、"""、"?" 和 "*"）會被底線（"_"
     ）取代。

   在 3.6 版的變更: 在一個已關閉的 ZipFile 上呼叫 "extract()" 將會引發
   "ValueError"。先前，會引發一個 "RuntimeError"。

   在 3.6.2 版的變更: *path* 參數接受一個 *path-like object*。

ZipFile.extractall(path=None, members=None, pwd=None)

   從封存檔案中解壓縮所有成員到目前工作目錄。*path* 指定一個不同的目錄
   來解壓縮。*members* 是可選的，且必須是 "namelist()" 回傳的串列的子
   集。*pwd* 是用於加密檔案的密碼，為一個 "bytes" 物件。

   警告:

     Never extract archives from untrusted sources without prior
     inspection. It is possible that files are created outside of
     *path*, for example, members that have absolute filenames or
     filenames with ".." components. This module attempts to prevent
     that. See "extract()" note.

   在 3.6 版的變更: 在一個已關閉的 ZipFile 上呼叫 "extractall()" 將會
   引發 "ValueError"。先前，會引發一個 "RuntimeError"。

   在 3.6.2 版的變更: *path* 參數接受一個 *path-like object*。

ZipFile.printdir()

   將封存檔案的內容目錄印出到 "sys.stdout"。

ZipFile.setpassword(pwd)

   設定 *pwd*（一個 "bytes" 物件）為解壓縮加密檔案的預設密碼。

ZipFile.read(name, pwd=None)

   回傳封存檔案中 *name* 檔案的位元組。*name* 是封存檔案中檔案的名稱，
   或是一個 "ZipInfo" 物件。封存檔案必須以讀取或附加模式開啟。*pwd* 是
   用於加密檔案的密碼，為一個 "bytes" 物件，如果指定，將會覆寫用
   "setpassword()" 設定的預設密碼。在一個使用 "ZIP_STORED"、
   "ZIP_DEFLATED"、"ZIP_BZIP2"、"ZIP_LZMA" 或 "ZIP_ZSTANDARD" 以外壓縮
   方法的 ZipFile 上呼叫 "read()" 將會引發 "NotImplementedError"。如果
   對應的壓縮模組不可用，也會引發一個錯誤。

   在 3.6 版的變更: 在一個已關閉的 ZipFile 上呼叫 "read()" 將會引發
   "ValueError"。先前，會引發一個 "RuntimeError"。

ZipFile.testzip()

   讀取封存檔案中的所有檔案，並檢查它們的 CRC 和檔案標頭。回傳第一個損
   壞檔案的名稱，否則回傳 "None"。

   在 3.6 版的變更: 在一個已關閉的 ZipFile 上呼叫 "testzip()" 將會引發
   "ValueError"。先前，會引發一個 "RuntimeError"。

ZipFile.write(filename, arcname=None, compress_type=None, compresslevel=None)

   將名為 *filename* 的檔案寫入封存檔案，並給予它封存名稱 *arcname*（
   預設情況下，這將與 *filename* 相同，但不含磁碟機代號且移除了開頭的
   路徑分隔符）。如果給定，*compress_type* 將覆寫為建構函式中
   *compression* 參數為新條目指定的值。同樣地，如果給定
   *compresslevel*，它將覆寫建構函式。封存檔案必須以 "'w'"、"'x'" 或
   "'a'" 模式開啟。

   備註:

     ZIP 檔案標準在歷史上並未指定元資料編碼，但為了互通性
     (interoperability) 強烈建議使用 CP437（原始的 IBM PC 編碼）。近期
     的版本允許（僅）使用 UTF-8。在本模組中，如果成員名稱包含任何非
     ASCII 字元，將會自動使用 UTF-8 來寫入。無法以 ASCII 或 UTF-8 以外
     的任何編碼寫入成員名稱。

   備註:

     封存名稱應相對於封存檔案的根目錄，也就是說，它們不應以路徑分隔符
     開頭。

   備註:

     如果 "arcname"（或在未給定 "arcname" 時的 "filename"）包含一個空
     位元組，封存檔案中該檔案的名稱將在空位元組處被截斷。

   備註:

     檔名中開頭的斜線可能導致在 Windows 系統上的某些 zip 程式中無法開
     啟該封存檔案。

   在 3.6 版的變更: 在以 "'r'" 模式建立的 ZipFile 或一個已關閉的
   ZipFile 上呼叫 "write()" 將會引發 "ValueError"。先前，會引發一個
   "RuntimeError"。

ZipFile.writestr(zinfo_or_arcname, data, compress_type=None, compresslevel=None)

   將一個檔案寫入封存檔案。內容為 *data*，它可以是一個 "str" 或一個
   "bytes" 實例；如果它是一個 "str"，它會先被編碼為 UTF-8。
   *zinfo_or_arcname* 是它在封存檔案中將被賦予的檔案名稱，或是一個
   "ZipInfo" 實例。如果它是一個實例，則至少必須給定檔名、日期和時間。
   如果它是一個名稱，則日期和時間被設為目前的日期和時間。封存檔案必須
   以 "'w'"、"'x'" 或 "'a'" 模式開啟。

   如果給定，*compress_type* 將覆寫為建構函式中 *compression* 參數為新
   條目指定的值，或覆寫 *zinfo_or_arcname*（如果它是一個 "ZipInfo" 實
   例）中的值。同樣地，如果給定 *compresslevel*，它將覆寫建構函式。

   備註:

     當傳遞一個 "ZipInfo" 實例作為 *zinfo_or_arcname* 參數時，所使用的
     壓縮方法將是給定的 "ZipInfo" 實例的 *compress_type* 成員中指定的
     方法。預設情況下，"ZipInfo" 建構函式將此成員設為 "ZIP_STORED"。

   在 3.2 版的變更: *compress_type* 引數。

   在 3.6 版的變更: 在以 "'r'" 模式建立的 ZipFile 或一個已關閉的
   ZipFile 上呼叫 "writestr()" 將會引發 "ValueError"。先前，會引發一個
   "RuntimeError"。

   在 3.14 版的變更: Now respects the "SOURCE_DATE_EPOCH" environment
   variable. If set, it uses this value as the modification timestamp
   for the file written into the ZIP archive, instead of using the
   current time.

ZipFile.mkdir(zinfo_or_directory, mode=511)

   在封存檔案內建立一個目錄。如果 *zinfo_or_directory* 是一個字串，則
   會在封存檔案內以 *mode* 引數中指定的模式建立一個目錄。然而，如果
   *zinfo_or_directory* 是一個 "ZipInfo" 實例，則 *mode* 引數會被忽略
   。

   封存檔案必須以 "'w'"、"'x'" 或 "'a'" 模式開啟。

   在 3.11 版被加入.

以下資料屬性也可用：

ZipFile.filename

   ZIP 檔案的名稱。

ZipFile.debug

   要使用的偵錯輸出層級。可以從 "0"（預設，無輸出）設定到 "3"（最多輸
   出）。偵錯資訊會被寫入到 "sys.stdout"。

ZipFile.comment

   與 ZIP 檔案關聯的註解，為一個 "bytes" 物件。如果為一個以 "'w'"、
   "'x'" 或 "'a'" 模式建立的 "ZipFile" 實例指派註解，其長度不應超過
   65535 位元組。超過此長度的註解將被截斷。


Path 物件
=========

class zipfile.Path(root, at='')

   從一個 "root" zipfile（它可以是一個 "ZipFile" 實例或一個適合傳遞給
   "ZipFile" 建構函式的 "file"）建構一個 Path 物件。

   "at" 指定此 Path 在 zipfile 中的位置，例如 'dir/file.txt'、'dir/'
   或 ''。預設為空字串，表示根目錄。

   備註:

     The "Path" class does not sanitize filenames within the ZIP
     archive. Unlike the "ZipFile.extract()" and
     "ZipFile.extractall()" methods, it is the caller's responsibility
     to validate or sanitize filenames to prevent path traversal
     vulnerabilities (for example, absolute paths or paths with ".."
     components). When handling untrusted archives, consider resolving
     filenames using "os.path.abspath()" and checking against the
     target directory with "os.path.commonpath()".

Path 物件公開了 "pathlib.Path" 物件的以下特性：

Path 物件可以使用 "/" 運算子或 "joinpath" 來遍歷。

Path.name

   最後的路徑元件。

Path.open(mode='r', *, pwd, **)

   在目前路徑上呼叫 "ZipFile.open()"。允許透過支援的模式 'r'、'w'、
   'rb'、'wb' 以讀取或寫入、文字或二進位模式開啟。當以文字模式開啟時，
   位置引數和關鍵字引數會被傳遞給 "io.TextIOWrapper"，否則會被忽略。
   "pwd" 是傳遞給 "ZipFile.open()" 的 "pwd" 參數。

   在 3.9 版的變更: 新增了對 open 的文字和二進位模式的支援。預設模式現
   在是文字模式。

   在 3.11.2 版的變更: "encoding" 參數可以作為位置引數提供，而不會導致
   "TypeError"，就像在 3.9 中一樣。需要與未修補的 3.10 和 3.11 版本相
   容的程式碼，必須將所有 "io.TextIOWrapper" 引數，包括 "encoding"，作
   為關鍵字傳遞。

Path.iterdir()

   列舉目前目錄的子項目。

Path.is_dir()

   如果目前情境參照到一個目錄，則回傳 "True"。

Path.is_file()

   如果目前情境參照到一個檔案，則回傳 "True"。

Path.is_symlink()

   如果目前情境參照到一個符號連結，則回傳 "True"。

   在 3.12 版被加入.

   在 3.13 版的變更: 先前，"is_symlink" 會無條件地回傳 "False"。

Path.exists()

   如果目前情境參照到 zip 檔案中的一個檔案或目錄，則回傳 "True"。

Path.suffix

   最後一個元件中以點分隔的最後一部分（如果有的話）。這通常被稱為副檔
   名。

   在 3.11 版被加入: 新增 "Path.suffix" 特性。

Path.stem

   最後的路徑元件，不含其後綴。

   在 3.11 版被加入: 新增 "Path.stem" 特性。

Path.suffixes

   路徑的後綴串列，通常稱為副檔名。

   在 3.11 版被加入: 新增 "Path.suffixes" 特性。

Path.read_text(*, **)

   將目前檔案讀取為 unicode 文字。位置引數和關鍵字引數會被傳遞給
   "io.TextIOWrapper"（但 "buffer" 會根據情境隱式處理）。

   在 3.11.2 版的變更: "encoding" 參數可以作為位置引數提供，而不會導致
   "TypeError"，就像在 3.9 中一樣。需要與未修補的 3.10 和 3.11 版本相
   容的程式碼，必須將所有 "io.TextIOWrapper" 引數，包括 "encoding"，作
   為關鍵字傳遞。

Path.read_bytes()

   將目前檔案讀取為位元組。

Path.joinpath(*other)

   回傳一個新的 Path 物件，並將每個 *other* 引數加入。以下是等效的：

      >>> Path(...).joinpath('child').joinpath('grandchild')
      >>> Path(...).joinpath('child', 'grandchild')
      >>> Path(...) / 'child' / 'grandchild'

   在 3.10 版的變更: 在 3.10 之前，"joinpath" 沒有文件記載，且只接受一
   個參數。

zipp 專案為舊版 Python 提供了最新的 path 物件功能的向後移植版本。使用
"zipp.Path" 來取代 "zipfile.Path" 以提早使用變更。


PyZipFile 物件
==============

"PyZipFile" 建構函式接受與 "ZipFile" 建構函式相同的參數，以及一個額外
的參數 *optimize*。

class zipfile.PyZipFile(file, mode='r', compression=ZIP_STORED, allowZip64=True, optimize=-1)

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

   在 3.4 版的變更: ZIP64 擴充預設為啟用。

   實例除了 "ZipFile" 物件的方法之外，還有一個額外的方法：

   writepy(pathname, basename='', filterfunc=None)

      搜尋 "*.py" 檔案，並將對應的檔案加入封存檔案。

      如果傳給 "PyZipFile" 的 *optimize* 參數未被給定或為 "-1"，則對應
      的檔案是一個 "*.pyc" 檔案，如有必要則進行編譯。

      如果傳給 "PyZipFile" 的 *optimize* 參數為 "0"、"1" 或 "2"，則只
      有具有該最佳化層級（參閱 "compile()"）的檔案會被加入封存檔案，如
      有必要則進行編譯。

      如果 *pathname* 是一個檔案，檔名必須以 ".py" 結尾，且只有（對應
      的 "*.pyc"）檔案會被加到頂層（沒有路徑資訊）。如果 *pathname* 是
      一個不以 ".py" 結尾的檔案，將會引發 "RuntimeError"。如果它是一個
      目錄，且該目錄不是一個套件目錄，則所有 "*.pyc" 檔案都會被加到頂
      層。如果該目錄是一個套件目錄，則所有 "*.pyc" 都會以檔案路徑的形
      式被加到套件名稱下，且如果任何子目錄是套件目錄，所有這些都會以排
      序後的順序遞迴地加入。

      *basename* 僅供內部使用。

      *filterfunc*，如果給定，必須是一個接受單一字串引數的函式。每個路
      徑（包括每個單獨的完整檔案路徑）在被加入封存檔案之前都會被傳遞給
      它。如果 *filterfunc* 回傳一個假值，該路徑將不會被加入，且如果它
      是一個目錄，其內容將被忽略。例如，如果我們的測試檔案要嘛在
      "test" 目錄中，要嘛以字串 "test_" 開頭，我們可以使用一個
      *filterfunc* 來排除它們：

         >>> zf = PyZipFile('myprog.zip')
         >>> def notests(s):
         ...     fn = os.path.basename(s)
         ...     return (not (fn == 'test' or fn.startswith('test_')))
         ...
         >>> zf.writepy('myprog', filterfunc=notests)

      "writepy()" 方法會建立帶有如下檔名的封存檔案：

         string.pyc                   # 頂層名稱
         test/__init__.pyc            # 套件目錄
         test/testall.pyc             # 模組 test.testall
         test/bogus/__init__.pyc      # 子套件目錄
         test/bogus/myfile.pyc        # 子模組 test.bogus.myfile

      在 3.4 版的變更: 新增 *filterfunc* 參數。

      在 3.6.2 版的變更: *pathname* 參數接受一個 *path-like object*。

      在 3.7 版的變更: 遞迴會對目錄條目進行排序。


ZipInfo 物件
============

"ZipInfo" 類別的實例由 "ZipFile" 物件的 "getinfo()" 和 "infolist()" 方
法回傳。每個物件儲存關於 ZIP 封存檔案單一成員的資訊。

有一個類別方法可以為檔案系統中的檔案建立一個 "ZipInfo" 實例：

classmethod ZipInfo.from_file(filename, arcname=None, *, strict_timestamps=True)

   為檔案系統上的一個檔案建構一個 "ZipInfo" 實例，為將其加入 zip 檔案
   做準備。

   *filename* 應為檔案系統上一個檔案或目錄的路徑。

   如果指定了 *arcname*，它將被用作封存檔案內的名稱。如果未指定
   *arcname*，名稱將與 *filename* 相同，但會移除任何磁碟機代號和開頭的
   路徑分隔符。

   *strict_timestamps* 引數，當設為 "False" 時，允許壓縮早於
   1980-01-01 的檔案，代價是將時間戳記設為 1980-01-01。對於晚於
   2107-12-31 的檔案，也會有類似的行為，時間戳記同樣會被設為極限值。

   在 3.6 版被加入.

   在 3.6.2 版的變更: *filename* 參數接受一個 *path-like object*。

   在 3.8 版的變更: 新增了 *strict_timestamps* 僅限關鍵字參數。

實例具有以下方法和屬性：

ZipInfo.is_dir()

   如果此封存成員是一個目錄，則回傳 "True"。

   這會使用條目的名稱：目錄應總是以 "/" 結尾。

   在 3.6 版被加入.

ZipInfo.filename

   封存檔案中檔案的名稱。

ZipInfo.date_time

   封存成員的最後修改時間和日期。這是一個包含六個值的元組，代表 ZIP 檔
   案中中央目錄的「最後 [修改] 檔案時間」和「最後 [修改] 檔案日期」欄
   位。

   該元組包含：

   +---------+----------------------------+
   | 索引    | 值                         |
   |=========|============================|
   | "0"     | 年（>= 1980）              |
   +---------+----------------------------+
   | "1"     | 月（從 1 開始）            |
   +---------+----------------------------+
   | "2"     | 日（從 1 開始）            |
   +---------+----------------------------+
   | "3"     | 時（從 0 開始）            |
   +---------+----------------------------+
   | "4"     | 分（從 0 開始）            |
   +---------+----------------------------+
   | "5"     | 秒（從 0 開始）            |
   +---------+----------------------------+

   備註:

     The ZIP format supports multiple timestamp fields in different
     locations (central directory, extra fields for NTFS/UNIX systems,
     etc.). This attribute specifically returns the timestamp from the
     central directory. The central directory timestamp format in ZIP
     files does not support timestamps before 1980. While some extra
     field formats (such as UNIX timestamps) can represent earlier
     dates, this attribute only returns the central directory
     timestamp.The central directory timestamp is interpreted as
     representing local time, rather than UTC time, to match the
     behavior of other zip tools.

ZipInfo.compress_type

   封存成員的壓縮型別。

ZipInfo.comment

   個別封存成員的註解，為一個 "bytes" 物件。

ZipInfo.extra

   擴充欄位資料。PKZIP Application Note 包含一些關於此 "bytes" 物件中
   所含資料內部結構的註解。

ZipInfo.create_system

   建立 ZIP 封存檔案的系統。

ZipInfo.create_version

   建立 ZIP 封存檔案的 PKZIP 版本。

ZipInfo.extract_version

   解壓縮封存檔案所需的 PKZIP 版本。

ZipInfo.reserved

   必須為零。

ZipInfo.flag_bits

   ZIP 旗標位元。

ZipInfo.volume

   檔案標頭的磁碟空間編號。

ZipInfo.internal_attr

   內部屬性。

ZipInfo.external_attr

   外部檔案屬性。

ZipInfo.header_offset

   到檔案標頭的位元組偏移量。

ZipInfo.CRC

   未壓縮檔案的 CRC-32。

ZipInfo.compress_size

   壓縮資料的大小。

ZipInfo.file_size

   未壓縮檔案的大小。


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

"zipfile" 模組提供了一個簡單的命令列介面，用以與 ZIP 封存檔案互動。

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

   $ python -m zipfile -c monty.zip spam.txt eggs.txt

傳遞一個目錄也是可以的：

   $ python -m zipfile -c monty.zip life-of-brian_1979/

如果你想將一個 ZIP 封存檔案解壓縮到指定的目錄，請使用 "-e" 選項：

   $ python -m zipfile -e monty.zip target-dir/

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

   $ python -m zipfile -l monty.zip


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

-l <zipfile>
--list <zipfile>

   列出 zipfile 中的檔案。

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

   從來源檔案建立 zipfile。

-e <zipfile> <output_dir>
--extract <zipfile> <output_dir>

   將 zipfile 解壓縮到目標目錄。

-t <zipfile>
--test <zipfile>

   測試 zipfile 是否有效。

--metadata-encoding <encoding>

   為 "-l"、"-e" 和 "-t" 指定成員名稱的編碼。

   在 3.11 版被加入.


解壓縮的陷阱
============

zipfile 模組中的解壓縮可能會由於下面列出的一些陷阱而失敗。


來自檔案本身
------------

解壓縮可能由於不正確的密碼 / CRC 校驗和 / ZIP 格式或不支援的壓縮方法 /
解密而失敗。


檔案系統限制
------------

超出不同檔案系統的限制可能導致解壓縮失敗。例如目錄條目中允許的字元、檔
名長度、路徑名長度、單一檔案的大小以及檔案數量等。


資源限制
--------

記憶體或磁碟空間不足會導致解壓縮失敗。例如，解壓縮炸彈（又稱 ZIP bomb
）適用於 zipfile 函式庫，可能導致磁碟空間耗盡。


中斷
----

在解壓縮過程中斷，例如按下 control-C 或終止解壓縮過程，可能導致封存檔
案解壓縮不完整。


解壓縮的預設行為
----------------

不了解預設的解壓縮行為可能導致非預期的解壓縮結果。例如，當兩次解壓縮同
一個封存檔案時，它會不經詢問就覆寫檔案。
