概要
マルチ プラットフォームに向け仕様改定があったはずなのに、未だに思ったほど普及が進んでいない様に思える、UNICODE ZIP。コロナの禍中で在宅ワークも増えて、ZIPファイルでの情報交換がより増えているはずで、UNICODE ZIPの必要性はより高まっていると思う。
ふとmacOS周りの事情やマルチプラット フォームのアーカイバが気になっり、
そもそも JavaのUNICODE ZIPの対応状況はどうなのだろう? と思い、軽く調べてみた。
結論
- Java 7 以降はUNICODE ZIPの圧縮に対応している。
- JDK 6 で ビルドしたプログラムでもJRE 7 以降で実行すれば、UNICODE ZIPが作れる。
targetオプションなどで 7以上を対象にするのが望ましそう
- JDK 6 で ビルドしたプログラムでもJRE 7 以降で実行すれば、UNICODE ZIPが作れる。
- Java 6 以前は非対応。Apach Commons Compressで対応可能。
- WEBアプリでのZIPの文字化け対策は、適切にUNICODE ZIPをしっかり使うこと。
- WEBアプリはマルチ プラットフォームがそもそもの本懐。
- WEBアプリが今更MS932で圧縮なんてしてはいけない。
( 馬鹿げている と言っても過言ではないくらい、随分と昔に対応されているため) - UNICODE ZIPに対応していない、旧時代の圧縮解凍ソフトを使うのを辞める。
- 解凍に対応:Windows Explorer, 7zip, Explzh, ...
- 解凍に非対応:Lhasa, Lhaplus
※ 窓の杜さん、マジでいつまでもサイトTOPのオススメ ソフトに、
UNICODE ZIPに対応していないLhaplusを掲載するの辞めてください。。。
せっかくWindows 8からExplorerがUNICODE ZIPに対応しているのに、完全にスポイルされ続しているので。。。
検証
確認環境
- Windows 10
- ビルド
- JDK 6, 7,
- Open JDK 7, 14
- 実行
- JRE 6, 7, 8
- Open JDK 7, 14
確認方法
- Javaで圧縮するサンプル プログラムを作成。
- 各JDKに同梱されているJREなどでそれぞれ実行し、ファイル名に日本語を含むサンプル ファイルを圧縮。
- 7zipで開き、特性カラムを確認。
- UNICODEの記載がある
⇒ Language encoding flag (EFS) が立っている
⇒ 正常なUNICODE ZIP - UNICODEの記載がない
⇒ Language encoding flag (EFS) が立っていない
⇒ 旧来のZIP
(OSの文字コードに依存するため、圧縮時と解凍時のOSのシステム文字コードが一致している必要がある)
- UNICODEの記載がある
参考資料
-
UNICODE ZIPに対応した圧縮/解凍ソフトについて - Qiita
https://qiita.com/libraplanet/items/97b1d1fbdc19b07bdf27
※ 手前味噌 -
java.util.zip (Java Platform SE 6)
https://docs.oracle.com/javase/jp/6/api/java/util/zip/package-summary.html
※ UNICODE ZIPへの言及無し -
java.util.zip (Java Platform SE 7 )
https://docs.oracle.com/javase/jp/7/api/java/util/zip/package-summary.htmlPKWARE ZIP File Format Specification の「APPENDIX D」- UTF-8 を使用して ZIP エントリファイル名およびコメントフィールドをエンコードする言語エンコーディングフラグ (EFS)。
-
java.util.zip (Java Platform SE 8 )
https://docs.oracle.com/javase/jp/8/docs/api/java/util/zip/package-summary.htmlPKWARE ZIP File Format Specificationの「APPENDIX D」- UTF-8を使用してZIPエントリ・ファイル名およびコメント・フィールドをエンコードする言語エンコーディング・フラグ(EFS)。
圧縮結果表
閾値が割と見えたので、総当たりは行っていない。
(JDKを揃えるのが大変なので)
ビルド (javac) |
実行 (java) |
Language encoding flag (EFS) |
備考 |
---|---|---|---|
JDK 6 | JRE 6 | なし | NG UTF-8のOSで圧縮しただけのZIP状態 |
JDK 6 | JRE 7 | あり | OK |
JDK 6 | JRE 8 | あり | OK |
JDK 6 | Open JDK 7 | あり | OK |
JDK 6 | Open JDK 14 | あり | OK |
JDK 7 | JRE 6 | - | 実行不可 |
JDK 7 | JRE 7 | あり | OK |
JDK 7 | JRE 8 | あり | OK |
JDK 7 | Open JDK 7 | あり | OK |
JDK 7 | Open JDK 14 | あり | OK |
Open JDK 7 | JRE 6 | - | 実行不可 |
Open JDK 7 | JRE 7 | あり | OK |
Open JDK 7 | JRE 8 | あり | OK |
Open JDK 7 | Open JDK 7 | あり | OK |
Open JDK 7 | Open JDK 14 | あり | OK |
Open JDK 14 | JRE 6 | - | 実行不可 |
Open JDK 14 | JRE 7 | - | 実行不可 |
Open JDK 14 | JRE 8 | - | 実行不可 |
Open JDK 14 | Open JDK 7 | - | 実行不可 |
Open JDK 14 | Open JDK 14 | あり | OK |
JavaはASCII文字のファイル名の場合でも、EFSを付ける様子。
7Zipは非ASCII文字以外を含む場合のみのため、ちょっとした違い。
ただし、UNICODE ZIPはUTF-8を使用しており、ASCII部分には互換があるため、EFSの有無はあまり影響は無い。
圧縮結果表 (Linux)
ちょうど手元にUbuntu 20をインストールした仮想マシンがあったので、ついうっかりLinux版OpenJDKも軽く試してみた。
ビルド (javac) |
実行 (java) |
Language encoding flag (EFS) |
備考 |
---|---|---|---|
Windows JDK 6 |
Linux Open JDK 7 |
あり | OK |
Windows JDK 6 |
Linux Open JDK 8 |
あり | OK |
Windows JDK 6 |
Linux Open JDK 14 |
あり | OK |
Windows JDK 7 |
Linux Open JDK 7 |
あり | OK |
Windows JDK 7 |
Linux Open JDK 8 |
あり | OK |
Windows JDK 7 |
Linux Open JDK 14 |
あり | OK |
Windows Open JDK 14 |
Linux Open JDK 7 |
あり | OK |
Windows Open JDK 14 |
Linux Open JDK 8 |
あり | OK |
Windows Open JDK 14 |
Linux Open JDK 14 |
あり | OK |
Linux Open JDK 7 |
Windows JRE 6 |
- | 実行不可 |
Linux Open JDK 7 |
Windows JRE 7 |
あり | OK |
Linux Open JDK 7 |
Windows JRE 8 |
あり | OK |
Linux Open JDK 7 |
Windows Open JDK 7 |
あり | OK |
Linux Open JDK 7 |
Windows Open JDK 14 |
あり | OK |
Linux Open JDK 7 |
Linux Open JDK 7 |
あり | OK |
Linux Open JDK 7 |
Linux Open JDK 8 |
あり | OK |
Linux Open JDK 7 |
Linux Open JDK 14 |
あり | OK |
Linux Open JDK 8 |
Windows JRE 6 |
- | 実行不可 |
Linux Open JDK 8 |
Windows JRE 7 |
- | 実行不可 |
Linux Open JDK 8 |
Windows JRE 8 |
あり | OK |
Linux Open JDK 8 |
Windows Open JDK 7 |
- | 実行不可 |
Linux Open JDK 8 |
Windows Open JDK 14 |
あり | OK |
Linux Open JDK 8 |
Linux Open JDK 7 |
- | 実行不可 |
Linux Open JDK 8 |
Linux Open JDK 8 |
あり | OK |
Linux Open JDK 8 |
Linux Open JDK 14 |
あり | OK |
Linux Open JDK 14 |
Windows JRE 6 |
- | 実行不可 |
Linux Open JDK 14 |
Windows JRE 7 |
- | 実行不可 |
Linux Open JDK 14 |
Windows JRE 8 |
- | 実行不可 |
Linux Open JDK 14 |
Windows Open JDK 7 |
- | 実行不可 |
Linux Open JDK 14 |
Windows Open JDK 14 |
あり | OK |
Linux Open JDK 14 |
Linux Open JDK 7 |
- | 実行不可 |
Linux Open JDK 14 |
Linux Open JDK 8 |
- | 実行不可 |
Linux Open JDK 14 |
Linux Open JDK 14 |
あり | OK |
概ねWindows版と同様の想定通りの結果だったので、macのJavaでも同様になる期待が高い。
圧縮検証プログラムのサンプルコード
急ごしらえのため、雑コード
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.IOException;
import java.util.zip.ZipOutputStream;
import java.util.zip.ZipEntry;
public class Main {
private static void closeQuietly(final Closeable o) {
try{
if(o != null) {
o.close();
}
} catch(Exception e){
}
}
public static void main(String[] args) throws IOException {
System.out.println("hoge");
System.out.println("java version=" + System.getProperty("java.version"));
System.out.println("args[0]=" + args[0]);
System.out.println("args[1]=" + args[1]);
debugZipArc(args[0], args[1]);
System.exit(0);
}
private static void zipArc(final ZipOutputStream zoStream, final File obj, final String parentDirPath) throws IOException {
final String currentName = parentDirPath + obj.getName();
if(obj.isFile()) {
FileInputStream fiStream = null;
try {
byte buf[] = new byte[4096];
int len;
ZipEntry entry = new ZipEntry(currentName);
fiStream = new FileInputStream(obj);
zoStream.putNextEntry(entry);
while((len = fiStream.read(buf)) > 0) {
zoStream.write(buf, 0, len);
}
} finally {
closeQuietly(fiStream);
}
} else if(obj.isDirectory()) {
for (File child : obj.listFiles()) {
zipArc(zoStream, child, currentName + "/");
}
}
}
private static void debugZipArc(final String inDirPath, final String outFilePath) throws IOException {
FileOutputStream foStream = null;
ZipOutputStream zoStream = null;
try {
File obj;
foStream = new FileOutputStream(outFilePath);
zoStream = new ZipOutputStream(foStream);
obj = new File(inDirPath);
zipArc(zoStream, obj, "");
} finally {
closeQuietly(zoStream);
closeQuietly(foStream);
}
}
}
補足① JRE6とサロゲート ペア
解凍ツールによっては任意の文字コードでファイル名を展開してくれるものもある。
JRE6ではUTF-8で格納はされている様だけど。👺
と 𩸽
が化けてしまっている。サロゲート ペアを処理する能力は持ち合わせていないのかな、と思われる。
(JavaVMの問題?)
補足② UNICODE絵文字の表示能力
手元のマシンによっては、UNICODE絵文字が豆腐になるものもあった。同じWindows10、同じ7zipで豆腐になる、ならないがあり、またWinRARでも同様だった。なので、フォントのインストール状況の差異で表示だけの問題かと思われる。
(調査中)
補足③ UNICODE対応
それまでシステム文字コードで処理していたのが、国際対応によってUNICODEに置き換わっているものは、ZIPに限らず色々ある。UNICODE ZIPに対応するということは、それらの歩みと同じであり、国際文字対応に於いて非常に意味のある対応である。
※ 日本語文字も国際文字である。
- 実行ファイル(ネイテイブ) ⇒ Win20000あたりから、内部文字コードがUNICODE (WCHAR)での処理に対応。
- 実行ファイル(VM) ⇒ Java、.NET CLRはUNICODE (UCS2)で処理。
- ID3Tag ⇒ 2.3以降でUNICODEに対応。
- APE Tag ⇒ APEv2でUNICODEに対応。
UNICODEに対応することでシステム文字コードに関わらず、一意の処理で国際文字を扱うことができる。
感想
Javaが割と真っ当にUNICODE ZIPに対応していたのは驚いた。そのため、それなのに未だにUNICODE ZIPが普及出来ていないのはなぜ?と、更に謎が深まった。りあえずJava 7以降であれば標準機能でUNICODE ZIPが使える様なので、マルチ プラットフォームなZIPユーティリティを作るならJavaが結構良さげに感じた。
また、OS側のZIP圧縮機能でUNICODE ZIPに対応しないのもなぜだろう。恐らくmacの方もwinやjavaなどと同様にシステム文字コードで圧縮しただけの、従来の圧縮をしてるだけの様子。しかし、そもそも ZIPファイルはmacのファイル システム向けではない 以上、クロス プラットフォームを無視できないはずなのに、macですらもUNICODE ZIPにしないのがホント謎。
最もmacの場合、ZIPファイルにリソース フォークぶっ込んできている段階で、ルール違反感も否めない。ファイル構成を仕様に盛り込んでる段階で、JARやOffice Open XMLみたいに、表向きだけでもZIPを名乗るの辞めたほうが良いじゃないの?とか思ったりしないでもない。
参考
-
Application Note Archives - PKZIP & SecureZIP
https://support.pkware.com/home/pkzip/developer-tools/appnote/application-note-archives -
java.util.zip (Java Platform SE 6)
https://docs.oracle.com/javase/jp/6/api/java/util/zip/package-summary.html -
java.util.zip (Java Platform SE 7 )
https://docs.oracle.com/javase/jp/7/api/java/util/zip/package-summary.html -
java.util.zip (Java Platform SE 8 )
https://docs.oracle.com/javase/jp/8/docs/api/java/util/zip/package-summary.html -
OpenJDK
https://openjdk.java.net/