Mac OS Xファイル名の正規化

Mac OS Xのファイル名の文字エンコーディングはUTF-8になってる。このファイル名をEUC-JPとかの他のエンコーディングに変換するときに、そのままだとおかしくなる。

どうも原因は、Mac OS Xではひらがなやカタカナの濁音や半濁音を表すのに、二つの文字を組み合わせるようにしているからのよう。EUC-JPではそれらの文字は一つの文字として扱うようにしているので、うまく変換できない。ためしに再現してみるのに、以下のようなPerlのコードを用意してみた。

use warnings;
use strict;
use Encode;
use Unicode::Normalize;

my $str  = "スマイルギャング";
my $norm = decode_utf8($str); 
my $osx  = NFD($norm);  # 濁音を二つの文字を組み合わせて表す

print encode('euc-jp', $norm, Encode::PERLQQ),"\n";
print encode('euc-jp', $osx, Encode::PERLQQ),"\n";

これを実行すると、

スマイルギャング
スマイルキ\x{3099}ャンク\x{3099}

となって、Mac OS Xのファイル名形式の文字列では、濁点の部分がEUC-JPに変換できてないことがわかる。んじゃ、Mac OS Xのファイル名を受け取ったときにほかのエンコーディング変換するにはどうすれば良いか。それにはUncode::Normalizeというモジュールが用意されているので、そのモジュールのNFKCという関数を使う。*1

use warnings;
use strict;
use Encode;
use Unicode::Normalize; # Unicodeの正規化を行うモジュール

my $str  = "スマイルギャング";
my $norm = decode_utf8($str); 
my $osx  = NFD($norm);  # 濁音を二つの文字を組み合わせて表す
   $osx  = NFKC($osx);  # 濁音を一文字で表すように変換

print encode('euc-jp', $norm, Encode::PERLQQ),"\n";
print encode('euc-jp', $osx, Encode::PERLQQ),"\n";

これで、

スマイルギャング
スマイルギャング

と表示されて、うまくEUC-JPに変換できた。

perl5.8のUnicodeサポートをかなり参考にしたので、くわしくはそちらで。

*1:上のコードでもこのモジュールをちゃっかり使ってるけど