探したのだけど作っている人がいなかったので、いわゆるutf-8-macと呼ばれるエンコーディングを追加するEncode::Encodingをつくりました。
https://github.com/tomi-ru/Encode-UTF8Mac
use Encode; use Encode::UTF8Mac; print Encode::encode('utf-8-mac', '蘄藭づけ'); use Path::Class; for my $entry (dir(".")->children) { my $filename = Encode::decode('utf-8-mac', $entry); }
反応みてPODちゃんと書いたらUPしようと思っている
→POD書いた. PODの方が少し整理されているのでわかりやすいかもしれない。
https://github.com/tomi-ru/Encode-UTF8Mac/blob/master/README
Macのファイル名はNFDされている
MacOSXでは文字コードはutf-8エンコーディングが使われているのだけど、UnicodeのNFDという正規化がされている。
具体的には、「だ」みたいな濁点つきカナが、「た」+「゛」の二つのUnicodeで表現されたりしてる。゛は「てん」で変換してでる濁点マーク(U+309B)ではなく、Unicodeで組み合わせる用として用意されている点(U+3099)。
ふつうにdecode('utf-8')すると「だ」が一文字じゃなく2つのUnicode、つまりlengthが2になるわけです。
なので、perlではファイル名はをdecodeしたあとUnicode::NormalizeのNFC関数(NFCは「た」+「゛」的なものを「だ」と扱う正規化名)を使ってくっつけるという方法が使えます。
use Encode; use Unicode::Normalize; my $filename = Encode::decode('utf-8', $filename); $filename = Unicode::Normalize::NFC($filename);
ちなみにファイルやディレクトリを作成する時は、NFDな(「だ」のようにくっついた)ものをわたしても、Mac側で勝手にNFCします。逆に言うと、ファイルシステムに出す時はNFD化して出す、ということはしなくてもいい。(けど作ったモジュールはencode時ちゃんとNFDしてる)
utf-8-macとは
ところが、UnicodeのNFCは「た」+「゛」→「だ」のようなそのままのものだけではなく、「蘄」を「福」とする、などの正規化も行なってしまいます。
Macでは「蘄」は使えないのか?というとそんなことはない。じゃあMacはどうしてるのかというと、特定の範囲はNFDしない、という特例付きでNFDしています。
http://developer.apple.com/library/mac/#qa/qa2001/qa1173.html
「HFS Plus は、Normal Form D の変形を使用しており、U+2000 から U+2FFF、U+F900 から U+FAFF、および U+2F800 から U+2FAFF は分解されません」
http://www.unicode.org/charts/PDF/U2000.pdf
http://www.unicode.org/charts/PDF/UF900.pdf
http://www.unicode.org/charts/PDF/U2F800.pdf
perlでutf-8-macなdecodeをしたい
じゃこれを判断して'蘄藭づけ.txt'みたいなファイルを'福神づけ.txt'じゃなくちゃんと'蘄藭づけ.txt'とdecodeするにはどうしたらよいかというと、osxに付属のiconvは独自に'utf-8-mac'というエンコーディングを使えるようにしてるらしく、それで変換すればいいらしい。
ただこれだけのために日ごろ使うことないText::Iconv使いたくないなあと。
そこで、特例の範囲をのぞいてNFC/NFDするエンコーディングを作りました。
なんで誰もつくってないんだ??!とおもったのですが、実際は、これら特例の範囲はほぼ超難しい漢字とかだけなので、影響するのはアジア圏の一部、ということで上記のUnicode::Normalize::NFC作戦でほぼ問題はないということから作られてなかったのかもしれないですね。
これがあれば、ファイル名を出し入れする時にEncodeのみでいけるので、使うエンコーディングは、日本語の場合こんな感じにするだけでよくなるんじゃないかと思います。
$encoding = do { if ($^O eq 'MSWin32') { 'cp932',; } elsif ($^O eq 'darwin') { 'utf-8-mac'; } else { 'utf-8'; } };