Pythonのzipfileライブラリはファイル名をUTF-8でエンコードするためWindowsのレガシーな解凍ソフトで解凍するとファイル名が文字化けします。ファイル名をShift-JISでエンコードする方法を紹介します。
世の中にはUTF-8で文字化けする解凍ソフトが残っている
2007年9月にZIPの仕様が追加され、エンコードをUTF-8に統一するための仕組みが完成してから、ZIPファイル名はUTF-8にエンコードすることがスタンダードになりつつあります。
しかし、日本国内では、UTF-8に未対応の解凍ソフト(+Lhaca Lhaplusなど)が大量に出回っており、使用されています。(WindowsのエクスプローラーだけでUTF-8のZIP解凍できるのに)
ファイル名をShift-JISでエンコードできるのか
各UTF-8未対応の解凍ソフトをアンインストールしてもらうわけにもいかないので、ファイル名をShift-JISでエンコードする方法を考えます。
まず、CPythonのzipfileライブラリのソースコードを見てみます。
CPython3.7では、454行目にファイル名のエンコードの関数があります。抜粋すると下記の通り。
def _encodeFilenameFlags(self): try: return self.filename.encode('ascii'), self.flag_bits except UnicodeEncodeError: return self.filename.encode('utf-8'), self.flag_bits | 0x800
まずASCIIでのエンコードを施行して、UnicodeEncodeErrorならば(=ASCIIでなければ)「UTF-8」でエンコードするようにハードコードされていました・・・
標準ライブラリをコピーしてShift-JISに変更する
あまりしたくないですが・・・標準ライブラリをコピーしてShift-JISに対応します。
454行目の'utf-8'
を'cp932'
に変更します。また、フラグ0x800
を削除します。
ちなみに、0x800
は、ZIPの規格で定められた「general purpose bit flag」のBit 11を1
にしています(16進数800
=2進数 100000000000
)。これは、前述のエンコードをUTF-8に統一するための仕組みで、規格では「Language encoding flag (EFS). If this bit is set, the filename and comment fields for this file MUST be encoded using UTF-8.」と定められており、UTF-8でなければ立ててはいけないフラグなので取り除いています。
zipfile.py
454-: return self.filename.encode('utf-8'), self.flag_bits | 0x800 454+: return self.filename.encode('cp932'), self.flag_bits
import
先を今回作ったzipfile.pyに変更します。
from .zipfile import ZipFile (略) with ZipFile(zb, mode='w') as zf: (略)
以上の対策で、Windowsのレガシーな解凍ソフトで解凍できるようになります。逆にLinuxなどではUTF-8しか対応していないため文字化けする可能性があります。
コメント