Mac で圧縮した zip を Windows の人に送ったら「ファイル名が 隲区アよ嶌_2026蟷エ8譛�.txt になってる」と返ってきた。逆に、Windows の人からもらった zip を Mac で開こうとしたら、解凍そのものが Illegal byte sequence で止まった。どちらもよくある話ではないでしょうか。
この 2 つ、症状は正反対に見えますが、原因は同じところにあります。ZIP ファイルの中の、たった 1 ビットです。
検体は macOS 26.5.2(ビルド 25F84)で自分で作って、バイト列まで確かめました。
先に結論
| 困っている場面 | やること |
|---|---|
| Mac で圧縮して Windows の人に送る | zip コマンドと Finder の「圧縮」は使わない。7z a -tzip 出力.zip 対象 で作る |
| Windows で作られた zip を Mac で開く | ダブルクリック(Finder)か unar で開く。unzip と 7z は使わない |
| もう展開してしまって名前が化けている | 元の zip が残っていればそちらから開き直す。名前だけ直すのは失敗することがあります |
ZIP はファイル名の文字コードを覚えていない
請求書_2026年8月.txt という 1 ファイルを macOS の zip コマンドで固めて、先頭を覗いてみます。
$ zip -q ../test.zip "請求書_2026年8月.txt"
$ xxd -l 48 ../test.zip
00000000: 504b 0304 0a00 0000 0000 7c0c 165d 2030 PK........|..] 0
00000010: 3a36 0600 0000 0600 0000 1900 1c00 e8ab :6..............
00000020: 8be6 b182 e69b b85f 3230 3236 e5b9 b438 ......._2026...8
この 48 バイトの中に、化ける理由が 2 か所そろっています。
1 つめは 3 行目の e8ab 8be6 b182 e69b b85f。これは「請求書_」を UTF-8 で符号化したバイト列そのものです。ZIP はファイル名を文字ではなくバイト列で持っています。
2 つめは 1 行目の 0000 7c0c のうち、先頭から 6 バイト目と 7 バイト目にあたる 0000。ここは汎用ビットフラグという 2 バイトの領域で、0000 は全ビットが 0、つまり何も宣言していない状態です。
もうお分かりでしょうか。この zip は、UTF-8 のバイト列を UTF-8 だと言わずに入れています。 受け取った側は、書いてないものは読めないので、自分の環境の既定で読むしかありません。日本語版 Windows ならそれは CP932(いわゆる Shift_JIS 系)です。同じことを手元でやってみると、
$ printf '請求書_2026年8月.txt' | iconv -f CP932 -t UTF-8
隲区アよ嶌_2026蟷エ8譛
iconv: iconv(): Illegal byte sequence
冒頭で見た 隲区アよ嶌 が出てきました。これが文字化けの正体です。
ZIP の仕様にも救済策はあって、汎用ビットフラグの bit 11 を立てると「このファイル名とコメントは UTF-8 です」という意味になります。問題は、macOS 標準の圧縮がこれを立てないことです。
化け方のパターン
UTF-8 のバイト列を CP932 として読むと、日本語はだいたいこう見えます。手元で実測した対応です。
| 元の名前 | 化けた見た目 |
|---|---|
| 請求書 | 隲区アよ嶌 |
| 写真 | 蜀咏悄 |
| あいうえお | 縺ゅ>縺�縺医♀ |
| 打ち合わせメモ | 謇薙■蜷医o縺帙Γ繝「 |
| ダウンロード | 繝繧ヲ繝ウ繝ュ繝シ繝� |
先頭が 縺・繧・繝・蜀・隲・謇 あたりで始まっていたら、この化け方だと思ってほぼ間違いありません。ひらがなが多い名前だと 縺 が、カタカナだと 繝 がよく顔を出します。
自分の zip がどちらなのか確かめる
フラグは 1 コマンドで見られます。先頭のヘッダの 6 バイト目から 2 バイトです。
$ xxd -s 6 -l 2 -p ファイル.zip
0000 ← bit 11 なし(宣言していない)
0008 ← bit 11 あり(UTF-8 だと宣言している)
手元の検体で確かめた結果です。
| 作り方 | 出力 | UTF-8 の宣言 |
|---|---|---|
macOS の zip コマンド |
0000 |
なし |
ditto -c -k(Finder の「圧縮」相当) |
0000 |
なし |
| 日本語 Windows 由来(ファイル名が CP932) | 0000 |
なし |
7z a -tzip |
0008 |
あり |
Python の zipfile |
0008 |
あり |
ここは私が最初に取り違えたところなので、注意点をひとつ。この 2 バイトはリトルエンディアン、つまり下位バイトが先に並びます。bit 11 は 0x0800 なので、xxd の出力では後ろの桁に 08 として現れます。前の桁が 08(0800)なのは別のビット(データディスクリプタの有無)で、UTF-8 とは関係ありません。
なお macOS の zipinfo(unzip -Z -v)にはこのフラグを表示する項目がありません。ファイル名長も CRC も出るのに、肝心のフラグだけ出ないのです。
送る側:Mac で、Windows でも化けない zip を作る
一番手軽なのは 7-Zip です。Homebrew で入ります。
$ brew install p7zip
$ 7z a -tzip 資料.zip 資料フォルダ
これで bit 11 が立ちます。実測したところ -mcu=on(UTF-8 を強制するオプション)を足しても出来上がりのバイト列は同じでした。既定で UTF-8 として宣言してくれます。
Python が入っているなら、標準ライブラリでも構いません。非 ASCII のファイル名なら自動で bit 11 を立ててくれます。
$ python3 -c "import shutil; shutil.make_archive('資料', 'zip', '資料フォルダ')"
Info-ZIP の zip には -UN=UTF8 という Unicode オプションがあって、これを勧めている記事も見かけます。私も最初はこれで済むと思っていました。ところが、
$ zip -q -UN=UTF8 ../test.zip "請求書_2026年8月.txt"
zip error: Invalid command arguments (short option 'N' not supported)
macOS に同梱されている zip(Apple 改変版の Zip 3.0)では、オプションとして受け付けてもらえません。Linux の Info-ZIP なら通る話が、Mac ではそのまま使えない例です。
受け取る側:Windows で作られた zip を Mac で開く
ファイル名が CP932 で入っている zip を、macOS 上の各ツールで展開して、出てきた名前をバイト列で照合しました。
| ツール | 結果 |
|---|---|
unzip |
展開できずに終了(exit 50) |
unzip -O CP932 |
オプションが存在しない(exit 10) |
ダブルクリック相当(ditto -x -k) |
正しく展開できる |
unar |
正しく展開できる |
7z x |
化ける |
意外だったのは unzip です。名前が化けるのではなく、1 ファイルも作られません。
$ unzip C_win_cp932.zip -d t50
Archive: C_win_cp932.zip
error: cannot create t50/?????_2026?N8??.txt
Illegal byte sequence
$ ls -a t50
. ..
「Mac で zip が解凍できない」という症状の一部は、実はこれです。壊れたファイルをもらったわけでも、パスワードが要るわけでもありません。
そして Linux でよく案内される unzip -O CP932 ですが、macOS の unzip にはこのオプション自体がありません。ヘルプにも文字コード系の -I / -O は 1 つも出てきません。同じ「UnZip 6.00」と名乗っていても中身が違うので、気をつけましょう。
7z x はもっとたちが悪い化け方をします。出てきた名前を 8 進表記で見ると、
\302\220¿\302\213\302\201\302\217\302\221_2026\302\224N8\302\214\302\216.txt
\302\220 は U+0090 の UTF-8 表現です。つまり手元の 7z(p7zip 17.05)は、宣言のないファイル名を 1 バイト = 1 文字の Latin-1 として読んでいます。CP932 として読み直せば戻る、という手も通用しない壊れ方です。日本語 Windows 由来の zip に 7z を使うのはやめておきましょう。
結局、Mac 側で無難なのは Finder でダブルクリックするか、unar を使うかの 2 つです。
$ brew install unar
$ unar 受け取った.zip
unar は文字コードを自動判別します。判別を外したときだけ -e CP932 のように明示すればよく、最初から付ける必要はありません(むしろ UTF-8 の zip に付けると化けます)。
もう展開してしまって、名前が化けているとき
化けた名前は、理屈のうえでは戻せます。「化けた文字列を CP932 で符号化し直して、それを UTF-8 として読む」と元に戻る。文字化け復元ツールがやっているのもこれです。
ただ、戻せない場合があります。手元の 8 例で試しました。
| 元の名前 | 復元 |
|---|---|
| 請求書 | 戻る |
| 写真 | 戻る |
| 打ち合わせメモ | 戻る |
| 見積り | 戻る |
| あいうえお | 戻らない |
| ダウンロード | 戻らない |
| 会議資料 | 戻らない |
| 請求書_2026年8月.txt | 戻らない |
理由は 1 つです。UTF-8 のバイト列の中には、CP932 として読むとどの文字にもならない並びが混じります。読めなかったバイトはそこで置換文字(? や �)に潰されて、元のバイトが失われる。失われたものは後から計算で戻せません。
先ほどの 請求書_2026年8月.txt がまさにそれで、月 の UTF-8 表現 e6 9c 88 の最後の 88 が CP932 として成立せず、そこで情報が落ちます。冒頭に出てきた 隲区アよ嶌_2026蟷エ8譛�.txt の、最後に混じっていた � がその落ちた場所です。3 文字だけの「請求書」なら戻るのに、日付を足したとたん戻らなくなる。
$ python3 -c "print('請求書_2026年8月.txt'.encode('utf-8').decode('cp932'))"
UnicodeDecodeError: 'cp932' codec can't decode byte 0x88 in position 20
なので、元の zip が残っているなら、名前を直そうとするより開き直すほうが確実です。上の受け取る側の表に戻って、Finder か unar でもう一度展開してください。復元ツールに頼るのは、zip が手元にもう無いときの最後の手段だと思っておくとよさそうです。
おまけ:濁点が分かれて見えるのは、別の話
ダウンロード が タ゛ウンロート゛ のように、濁点だけ後ろにずれて見えることがあります。これは文字コードの取り違えではなく、Unicode の正規化形の違いです。ダ を 1 文字(U+30C0)で持つのが NFC、タ + 結合用の濁点(U+30BF U+3099)の 2 文字で持つのが NFD。
よく「macOS は NFD だから化ける」と説明されます。私もそう覚えていました。ところが手元の APFS で確かめると、NFC で作れば NFC のまま、NFD で作れば NFD のまま残ります。
NFC で作成: e38380 e382a6 ...
NFD で作成: e382bf e38299 e382a6 ...
--- ディレクトリの中身 ---
e38380e382a6... NFC ダウンロード.txt ← 1 件だけ
ファイルは 1 つしかできません。NFD 名で作り直しても、先に作った NFC のファイルと同一とみなされて、名前は NFC のまま。APFS は正規化を区別しないけれど、作られたときの形は保存する、という挙動です。NFD へ強制変換していたのは HFS+ の時代の話でした。
とはいえ、よそから来た NFD の名前まで直してくれるわけではありません。そして NFD の名前は zip の中を素通りします。
unzip → ダウンロード.txt [NFD]
ditto -x -k → ダウンロード.txt [NFD]
Mac の画面上では NFC と見分けが付かないので気づけません。渡した先の環境で初めて濁点が分かれて見える、という順序になります。同じ NFD/NFC の話は rsync でファイル名が文字化けするときの対処(–iconv と macOS の NFD 問題) にも書いたので、ファイル転送で困っている方はそちらもどうぞ。
おまけ:__MACOSX フォルダが増えるのも同じ経路
Mac で圧縮した zip を Windows で開くと、__MACOSX というフォルダと ._ で始まるファイルが一緒に出てくる、という話もよく聞きます。中身を数えると、Finder の「圧縮」相当(ditto -c -k)は本体 1 件のほかにリソースフォーク用のエントリを 3 件詰めていました。開いた側にそれがそのまま出てくるわけです。zip コマンドで固めた場合はエントリが 1 件だけで、これは出ません。
7z a -tzip も付けないので、文字化け対策に切り替えると、こちらもついでに片付きます。
さいごに
冒頭の 隲区アよ嶌_2026蟷エ8譛�.txt も、Mac 側で出た Illegal byte sequence も、たどっていくと同じ 2 バイトのフラグ欄に行き着きます。原因はフラグ 1 ビット、と書くと拍子抜けしますが、その 1 ビットが無いせいで受け取り側は推測するしかなくなり、推測を外すと元に戻せなくなる。ZIP はもともとファイル名の文字コードを記録する場所を持たない形式で、bit 11 は後から継ぎ足された欄です。古い仕様と付き合うというのはこういうことなのだな、と実測しながら思いました。
送るときは 7z a -tzip、受け取るときは Finder か unar。この 2 つだけ覚えておけば、たいてい困りません。あとは Windows の実機で実際にどう並んで見えるのかを自分の目で確かめたいのですが、手元に Windows が無くて、そこはまだ宿題です。
あわせてどうぞ:CSV が Excel で文字化けする原因と直し方