mac 圧縮ファイル 解凍 windows 文字化けを標準コマンドで直す
Windowsの相手から届いたzipをMacで開いたら、フォルダ名が「蜀咏悄01」のような並びになっていた。あるいは途中まで展開されて止まった。mac 圧縮ファイル 解凍 windows 文字化けで検索して出てくる答えの多くは「解凍アプリを入れなさい」で終わっている。実際に手元のMacで確かめると、Macには最初から入っているコマンドが2つあり、同じzipに対してまったく違う結果を返す。入れるものはひとつもない。
以下の挙動はすべて macOS 26.6.2 の実機で、日本語Windowsが作るのと同じ形のzip(ファイル名がShift_JISのバイト列で、Unicodeの印が付いていないもの)に対して確かめた結果を書いている。
文字化けではなく「取り出せていない」ことがある
ターミナルの unzip で中身を一覧すると、想像どおり崩れた文字が並ぶ。問題はその次にある。展開しようとすると、名前が崩れたファイルができるのではなく、エラーで止まる。
$ unzip -d out windows_sjis.zip
error: cannot create out/・シ・ス・ス_2026・スN・スx.txt
Illegal byte sequence
checkdir error: cannot create out/・ス・ス・ス・ス・ス・ス・スc・ス^
Illegal byte sequence
Illegal byte sequence は、ファイルシステムが名前を受け付けなかったという意味になる。APFSはファイル名に正しいUTF-8を要求する。Shift_JISのバイト列はUTF-8として成立しないので、作成そのものが失敗する。つまり画面に出ているのは文字化けしたファイルではなく、存在しないファイルということになる。
ここが実務でいちばん危ない。半角英数の名前が混ざったzipでは、英数のファイルだけが取り出され、日本語名のファイルだけが落ちる。展開先のフォルダにはそれらしくファイルが並ぶので、エラー行を読み飛ばした人は作業が終わったと思って先に進む。取り出せた本数と一覧の本数を突き合わせるのが、いちばん安いふるい分けになる。
原因はzipの中の1ビット
zipの各項目には「汎用ビットフラグ」という短い印が付いている。11番目のビット(値でいえば 0x800)が立っていれば、ファイル名はUTF-8で入っているという宣言になる。立っていなければ、名前は昔のコードページで入っていることになり、実際には書き込んだ道具が使ったバイト列がそのまま入る。日本語Windowsの道具はShift_JISのバイト列を書き、この印を立てなかった。zipの中には、それがShift_JISだと書かれた場所がどこにもない。
だからMacの側は推測するしかない。他のOSのInfo-ZIPにはコードページを指定する -O があるが、Appleが配っている版にはそれがない。
$ unzip -O CP932 -l windows_sjis.zip
Usage: unzip [-Z] [-opts[modifiers]] file[.zip] [list] [-x xlist] [-d exdir]
$ echo $?
10
終了コード 10 は引数エラーを指す。バージョンを見ると UnZip 6.00 of 20 April 2009, by Info-ZIP, with modifications by Apple Inc. と出る。既存の -U -UU は、すでにUTF-8で入っている名前の扱いを変えるだけで、コードページを足す働きはない。指定して逃げる道は用意されていない。
もう1つ、zipという形式の側では救えない理由がある。この印が立っていないとき、仕様は解釈を読み手側に委ねている。読む機械はそれぞれ自分の言語設定で解釈するので、送り手の画面では正しく見えているzipが、別の言語設定の機械では崩れて見える。どちらの機械も仕様どおりに動いている。どう解釈してほしかったのかはzipの中のどこにも記録されていないので、いくら中を調べても分からない。外から、そのファイルの出どころを知っている人が教えるしかない。
展開する前に中を見ておく
展開してから壊れ方を確かめるのは、手順として遅い。一覧を出すだけならほとんど時間がかからず、疑問の大半はその時点で片が付く。
$ unzip -l suspect.zip | tail -3
最後の数行に項目の総数が出る。この数を控えておくことが、後で取りこぼしに気付ける唯一の手掛かりになる。展開先に並んだファイルの本数と突き合わせられるためになる。項目が数百ある大きなzipでは、いま気付くか、締め切りのあとに気付くかの差になる。
一覧はほかに2つのことも教えてくれる。__MACOSX/ で始まる項目があれば、そのzipはMacで作られていて、受け取る側には不要な付随情報が入っている。名前が異様に長い項目があれば、長さを測っておく価値がある。このファイルシステムの上限は255バイトではなく255文字になる。macOS 26.6.2 で実測すると、日本語255文字の名前は問題なく作れて、256文字で File name too long になった。日本語の名前がここに届くことは多くないが、Windowsの共有から来た深い階層に長い書類名が付くと届くことがある。そしてこの失敗もまた、うまくいっているように見える出力の中に、飛ばされた1項目として紛れ込む。
標準で入っている ditto なら通る
/usr/bin/ditto はどのMacにも入っている。先ほど unzip が拒否したのとまったく同じzipに対して実行した結果がこれになる。
$ ditto -x -k windows_sjis.zip out2
$ ls out2
打ち合わせ議事録
請求書_2026年度.txt
名前は正しく戻り、終了コードは 0 で、追加インストールは要らない。Windows由来のzipを日常的に受け取る立場なら、覚えるコマンドを unzip から ditto -x -k に変えるだけで、詰まりの大半が消えることになる。
| 道具 | Macに最初から入っているか | Shift_JIS名のzipでの結果 |
|---|---|---|
| unzip | 入っている | 日本語名の項目が Illegal byte sequence で失敗 |
| ditto -x -k | 入っている | 名前が正しく戻る |
| python3 | Command Line Toolsに含まれる | 通るが、コードページを自分で指定する必要がある |
| 市販・無料の解凍アプリ | 入っていない | 製品による。多くはコードページを選べる |
Appleの公式ガイドは、zipが開けないときの案内をこう書いている。
.zipファイルを開けない場合は、Macに解凍した項目を保存するための十分な領域があることを確認してください。.zipファイルをほかの人から受け取った場合は、ファイルに問題があることがあります。その人にファイルをもう一度圧縮して再送するよう依頼してください。 出典: support.apple.com
書かれているのは空き容量の確認と、送り直しの依頼だけになる。Shift_JIS名のzipはどちらにも当てはまらないし、送り手はWindowsのまま同じ道具で作り直しても同じものを送ってくる。公式の案内が届かない範囲だからこそ、この質問が何年も繰り返されている。
それでも直らないときはコードページを指定する
中国語や韓国語のWindowsから届いたzipは別のコードページを使っているので、自動の判別が外れることがある。そのときは自分で名指しする。/usr/bin/python3 は macOS 26.6.2 で 3.9.6 が入っており、Command Line Tools以外の追加は要らない。
import zipfile, os, unicodedata
z = zipfile.ZipFile("windows_sjis.zip")
for info in z.infolist():
name = info.filename.encode("cp437").decode("cp932")
name = unicodedata.normalize("NFC", name)
dest = os.path.join("out", name)
os.makedirs(os.path.dirname(dest), exist_ok=True)
with open(dest, "wb") as f:
f.write(z.read(info))
encode("cp437") が奇妙に見えるが、ここが要になる。zipが何も宣言していないため、Pythonはすでに生のバイト列をCP437として読んでしまっている。CP437に戻すと元のバイト列が復元され、そこに cp932 を当てて初めて日本語として読める。中国語なら gbk、韓国語なら cp949 に差し替える。末尾の normalize("NFC") の意味は後段で扱う。
MacからWindowsへ送るzipも化ける
同じ1ビットが、逆方向でも効いてくる。Macに入っている zip は Zip 3.0 (July 5th 2008), by Info-ZIP, with modifications by Apple Inc. で、日本語名のフォルダを固めて印を読み直すとこうなる。
$ zip -qr made_by_zip.zip 資料
0x0 資料/
0x0 資料/仕様書.txt
名前はUTF-8のバイト列で入っているのに、UTF-8だという印が立っていない。受け取ったWindowsは自分のコードページとして読むので、そこで化けることになる。Info-ZIPで印を立てる指定は、Appleの版では通らない。
$ zip -qr -UN=UTF8 out.zip 資料
zip error: Invalid command arguments (short option 'N' not supported)
ditto -c -k も印は同じく立たず、加えて __MACOSX の項目が増える。同じMacに入っているPythonのモジュールなら印が立つ。
$ python3 -m zipfile -c out.zip 資料
0x800 資料/
0x800 資料/仕様書.txt
一度きりの受け渡しなら、ファイル名を半角英数にしてしまうのがいちばん確実になる。何度も繰り返す業務なら、python3 -m zipfile -c を定型にしておくと、Windows側で開いた瞬間の化けが消える。
_MACOSX と . の始末
ditto -c -k --sequesterRsrc で作ったzipには __MACOSX/資料/._仕様書.txt のような項目が入る。中身はAppleが付けている付随情報で、受け取った相手には意味のないゴミになる。/usr/sbin/dot_clean は手元のフォルダで ._ ファイルを統合または削除する。新しく固めるときは zip -x '*.DS_Store' '__MACOSX/*' で最初から入れない。
濁点の綴りが2通りある問題
展開がきれいに終わっても、もう1つ残る落とし穴がある。濁点付きの文字には2通りの正しい書き方がある。合成済みなら「ぱぴぷ」は 7 バイト、分解形なら同じ見た目で 10 バイトになる。濁点が別の文字として入るためになる。
APFSはこの違いに鈍感で、しかも書かれた形をそのまま保つ。実測では、合成形で作ったファイルに分解形のパスで到達でき、分解形で作ったファイルは一覧に分解形のまま出て、合成形のパスでも開けた。つまりMacの上では1つの名前に見えるが、zipの中やLinuxのサーバー、Windowsの共有では2つの別名になる。
同じに見えるのに一致しない名前、同期フォルダに勝手に増える二重のファイル、誰も触っていないのにGitが報告する改名は、ほとんどがここから来ている。Gitには core.precomposeUnicode という設定がこのために用意されている。取り出す時点でNFCにそろえておけば、そもそも木の中に持ち込まずに済む。
送り手と決めておけること
ここまでの対処はすべて、届いたあとに手元で行うものになる。同じ相手と毎週やり取りしているなら、もっと手前で決めてしまうほうが安くつく。ただし、頼んでも効かない依頼と効く依頼がある。
同じ道具で圧縮し直してもらうのは効かない。道具は同じバイト列を書き、同じように印を立てないためになる。別の形式に変えてもらうのは、問題を移すだけになりやすい。名前の文字コードを宣言せずに格納する入れ物なら、どれも同じあいまいさを引き継ぐ。印はzipの仕組みであって、ほかの形式に同じ保証があるかどうかは確かめないと分からない。
効く依頼は2つある。1つは、zipの中のファイル名を半角英数にしてもらい、日本語の書類名は中身か添え文のほうに置いてもらうことになる。後戻りのように見えるが、失敗する余地がまるごと消える。長く続いている取引ほど自然にこの形に落ち着いていくのは、そのためになる。もう1つは、経路が許すならzipにせずフォルダのまま渡してもらうことになる。共有ドライブや転送サービスは、名前を文字として、文字コードを宣言した経路で運ぶためになる。
どちらも無理なら、受け取る側が引き受けることになる。うまく引き受けるというのは、解読の手順を書き留めておくということで、思い出し直すことではない。zipとコードページを渡すと正しい名前のフォルダが出てくる短い処理は、書いても15行ほどにしかならない。それがあるだけで、数か月ごとに同じ調べ物をやり直さずに済む。
手元の道具の配置から見えること
ここまでの手順はすべて、フォルダを見る操作とコマンドを打つ操作を行き来している。展開先を決めるのはフォルダ側、ditto を打つのはターミナル側、結果の本数を数えるのはまたフォルダ側になる。窓を切り替える回数が増えるほど、エラー行を読み飛ばす確率が上がる。取り出せた本数の突き合わせを飛ばしてしまう原因は、注意力ではなく配置にある。
フォルダとターミナルとAIが同じ窓にある状態なら、展開先のフォルダを見ながらそのままコマンドを打ち、出てきた本数をその場で見比べられる。何ができる道具なのかはできることにまとめてある。ほかのファイル管理と何が違うのかは他のファイル管理との比較で、費用の考え方は料金で確認できる。
道具を替えるかどうかの前に、まず ditto -x -k を試す価値がある。ここまでで示したとおり、化けの多くは道具の不足ではなく、選んだコマンドの違いで説明が付く。
よくある質問
Finderでダブルクリックすると開けるのに、ターミナルのunzipだと失敗するのはなぜですか?
中でやっていることが違うためになる。ターミナルの unzip は名前のバイト列をそのままファイルシステムに渡すので、UTF-8として成立しないバイト列で失敗する。macOSに組み込まれた展開処理は名前を先に解釈する。同じzipで実測すると、unzip は日本語名をすべて拒否し、ditto -x -k はすべて正しく取り出した。
名前が化けていると、ファイルの中身も壊れていますか?
中身は無事になる。壊れているのは名前だけで、圧縮されたデータには手が付いていない。だからコードページを指定して名前を読み直し、バイト列を書き出せば、そのまま使えるファイルが戻る。中身が壊れている場合はCRCの照合が合わずに別のエラーが出るので、症状で見分けられる。
Windowsの相手に送る前に、化けないzipかどうか確認できますか?
できる。各項目の汎用ビットフラグを読み、0x800 が立っていればUTF-8だと宣言されている。標準で入っているPythonの1行で出せるので、送る前に数秒で確認できる。立っていなければWindowsは自分のコードページで読むため、日本語名は化けることになる。
解凍アプリを買う価値はありますか?
コードページを手で選ぶ場面がどれくらいあるかで決まる。ときどき届く程度なら、何も入れずに ditto -x -k で足りる。複数の言語圏から毎週届くなら、コードページを選べる画面がある製品のほうが速い。価格も対応環境も製品ごとに違うので、標準のコマンドで足りるかどうかを先に確かめてから比べるとよい。