彷徨えるフジワラ

年がら年中さまよってます

Mac OS HFS+ におけるファイル名文字コード

Mercurialcase insensitive filesystem 周りの修正をする際に仕込んだ知識のまとめエントリその5。またもや case insensitive とは関係の無い話。

"\xc4\xb0" というバイト列を名前に持つファイルを生成する状況を想定する。

Windows において、PythonUnicode API を用いて確認した場合、デフォルトの文字コードに cp932 が設定されている日本語 Windows なら、半角カタカナ2文字『トー』(U+FF84 U+FF70) という名前のファイルが確認できる一方で、デフォルトの文字コードUTF-8 が設定されている環境であれば、トルコ語アルファベットの U+0130 を名前に持つファイルが確認される。

CygwinLinux 環境の場合は、どちらの名前として解釈されるかは、LC_CTYPE 環境変数等のロケール設定に応じて異なる。

# 僕の手元の Python@Linux では cp932 設定が期待通りにはならないけれど....

『バイト列 API を使用した場合には、ファイル名として指定したバイト列がそのまま返って来る』とか『Unicode API を使用した場合には、文字コード設定に応じて結果が異なる』という点では Windows/Cygwin/Linux の間に差異は無い。

Unix 系』である Mac OS の HFS+ ではどうか?と言うと、実は上記の環境とは異なる挙動に。

HFS+ は内部文字コードとして UTF-8 を前提としているらしく、与えられたファイル名に対して、以下のように振舞う模様。

  • UTF-8 的に適切なバイト列は、そのまま使用する
  • UTF-8 的に不適切なバイトは、"%02X" 形式に正規化

例えば、cp923 的には妥当な '\x82\xa0' (U+3042:『あ』) を名前に持つファイルを生成すると、'%82%A0' という妙チキリンな名前のファイルが生成されてしまう、という具合。

この名前は、Windows においてシステムデフォルトの文字コードには該当する文字が無い場合に "?" が返って来るというレベルの話ではなく、HDD 媒体に記録される段階で正規化されてしまうので、『Unicode API なら本来の値で読み出せる』などという回避ルートも存在しない。

文字通り、cp932 で『あ』という名前のファイルを作ろうとしたら、似ても似つかない『%82%A0』という名前のファイルが出来上がる、という挙動なのだ。

Python 利用の有無とも関係が無い

cp932 みたいに『多バイト文字の2バイト目に ASCII とカブる文字が来る』ような文字コードの場合、例えば 'A' と同じ '\x41' を持つ『ア』('\x83\x41') であれば、'%83A' みたいなゴッチャになった名前のファイルが出来上がる。

なんとも面倒な仕様だこと .... orz > HFS+

Unix 系 OS の場合、とりあえず "/" とかが絡まなければ、どんなバイト列のファイル名でも強引に扱えなくも無いのだけれど、Mac OS の場合は『扱えない』と覚悟しておいたほうが良さそう。

更に補足すると、一旦こうなってしまった場合、『ア』の2バイト目の '\x41' は単なる 'A' として扱われてしまうため、case insensitive filesystem な HFS+ としては、'a' に相当する '\x61' を2バイト目に持つ『ヂ』('\x83\x61') は、『ア』(のつもりで作ったファイル)と同じものとして扱ってしまう。

まぁ、何となく予想は出来たけどね .... orz > HFS+

ちなみに、この挙動の実現方式としては、『システムコールレベルの仕様』(※ どのファイルシステムに書き出す場合でも共通) な場合と、『HFS+ 固有の仕様』な場合が考えられるので、そのうち NFS クライアントに仕立てて挙動の確認をしてみるか。

ファイルシステム固有』だったりすると、仮想化ゲストが NFS サーバになることで、色々と回避するための仕組みを作ったりできるんだけどなぁ ....