1. 程式人生 > >【Python】使用 zipfile 解壓含有中文檔名的 zip 檔案

【Python】使用 zipfile 解壓含有中文檔名的 zip 檔案

問題

在使用 Python 內建標準庫 zipfile 解壓檔案時,如果壓縮檔案中有的檔案含有中文,那麼解壓後就會發現檔名中的中文部分是亂碼。例如我分別新建三個 txt 檔案:檔案1.txt檔案2.txt檔案3.txt,然後將這三個檔案壓縮到一個名為 檔案.zip 的壓縮檔案中。然後我們使用下面的程式碼來解壓:

import zipfile
with zipfile.ZipFile('檔案.zip', 'r') as f:
    f.extractall()

解壓的結果如圖所示,中文已成亂碼:
解壓結果

原因

原因很簡單,zipfile 會將所有檔名用 CP437 來編碼,官方說明如下:

There is no official file name encoding for ZIP files. If you have unicode file names, you must convert them to byte strings in your desired encoding before passing them to write(). WinZip interprets all file names as encoded in CP437, also known as DOS Latin.

解決

知道檔名用的編碼後,就可以使用對應的編碼來解碼了。也就是先用 CP437

編碼 encode 成 bytes,再以 gbk 格式解碼成中文 string。

有兩種解決方案,兩種方案都是使用 extract 方法而不是 extractall方法,都是對壓縮檔案內的檔名進行遍歷,逐個解壓。

Python 版本為 3.6.4 |Anaconda custom (64-bit)| (default, Jan 16 2018, 10:22:32) [MSC v.1900 64 bit (AMD64)]

方案 1

第一種方案的思路是:

  1. 將檔名正確解碼,並用解碼後的檔名建立一個新檔案。
  2. 開啟原檔案,即檔名亂碼的檔案。
  3. 將原檔案中的內容寫入到新檔案中。

對應的程式碼如下:

import shutil
import zipfile

with zipfile.ZipFile('檔案.zip', 'r') as zf:
    for fn in zf.namelist():
        right_fn = fn.encode('cp437').decode('gbk')  # 將檔名正確編碼
        with open(right_fn, 'wb') as output_file:  # 建立並開啟新檔案
            with zf.open(fn, 'r') as origin_file:  # 開啟原檔案
                shutil.copyfileobj(origin_file, output_file)  # 將原檔案內容複製到新檔案

方案 2

第二種方案思路就比較簡單了:

  1. 正常解壓檔案。
  2. 使用正確的檔名重新命名解壓的檔案。

對應的程式碼如下,這裡使用了 pathlib 庫,強烈推薦該庫!

from pathlib import Path
import zipfile

with zipfile.ZipFile('檔案.zip', 'r') as f:
    for fn in f.namelist():
        extracted_path = Path(f.extract(fn))
        extracted_path.rename(fn.encode('cp437').decode('gbk'))

END