読者です 読者をやめる 読者になる 読者になる

Encode::UTF8Mac

探したのだけど作っている人がいなかったので、いわゆる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とは

ところが、UnicodeNFCは「た」+「゛」→「だ」のようなそのままのものだけではなく、「蘄」を「福」とする、などの正規化も行なってしまいます。

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

perlutf-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';
    }
};