Java プログラムでファイル名のエンコードを UTF-8 指定で圧縮した ZIP ファイルを、Windows エクスプローラーや 7-Zip で解凍したところ、ファイル名が文字化けした際の調査メモです。
事象
Zip4jでファイル名を UTF-8 で ZIP 圧縮しました。
この ZIP ファイル内は以下のファイル構成になっています。
files
ああああ.txt
ですが、このファイルを Windows 10 のエクスプローラーや 7-Zip で解凍を試みたところ、この「ああああ.txt」が「縺ゅ≠縺ゅ≠.txt」に文字化けしてしまいました。
その ZIP ファイルをバイナリエディタで開いた結果がこちらです。
右側の表記は UTF-8 で表示させてみた内容であり、「ああああ.txt」が確認できることから、正常に UTF-8 で ZIP 内に書き込まれているようです。
EFS(Language encoding flag)とは?
ZIP の仕様が書かれている APPNOTE.txtを確認したところ、EFS(Language encoding flag) というものがありました。
APPNOTE.txt から引用すると、以下のように記載されています。
Bit 11: Language encoding flag (EFS). If this bit is set,
the filename and comment fields for this file
MUST be encoded using UTF-8. (see APPENDIX D)
要約すると、このフラグが有効の場合はファイル名やコメントは UTF-8 でエンコードしなければならないとのことです。
EFS はローカルファイルヘッダとセントラルディレクトリヘッダの汎用目的ビットフラグ(general purpose bit flag)で設定します。
前述のスクリーンショットの赤枠が該当箇所です。
すべて 0 になっていることから Zip4j が圧縮した ZIP ファイルには EFS は設定されていないようです。
実験
自分は Windows のエクスプローラーや 7-Zip はこの EFS が有効の際は UTF-8 として判断して解凍する実装になっているのではという仮説を立てました。
そこで該当ZIPファイルをバイナリエディタで EFS を有効にしてみることにしました。
仕様書には 11 と記載されているので、右から 0 からカウントを始めて 12 番目を 1 にしました。
0000 1000 0000 0000
ZIP はリトルエンディアンなので順序を変更します。
0000 0000 0000 1000
16進数に変更します。
00 08
結果、以下のスクリーンショットのように、マルチバイト文字を使っているファイル名のローカルファイルヘッダとセントラルディレクトリヘッダの汎用目的ビットフラグを「00 08」に上書きしてみました。
その結果、Windows 10 のエクスプローラーと 7-Zip で解凍を試みたところ、文字化けせずに解凍することに成功しました。
このことから、Windows 10 のエクスプローラーと 7-Zip は EFS が有効であればファイル名は UTF-8 として解凍する実装になっているのではと考えています。
補足
- なお、Windows 7 のエクスプローラーは更新プログラムを適用しないと、UTF-8 のファイル名である ZIP ファイルの解凍に対応していないそうです。