perlで扱うutf8フラグやutf-8-macの小ネタ

2014/1/12 [17:40:36] (日) 天気

perlのスクリプトでmacの日本語ファイル名がマッチしない、という話題がtwitterに上がっていた。ネタ的に面白そうなので自分でも試してみたところ、macの日本語ファイル名が正規表現で一致しないことに気づいて、ちょっと吃驚。


フォルダには

・test.txt

・あいうえお

・ペダル

・memo.txt

など、アルファベットのファイルや日本語ファイル・フォルダが雑多に入っている。

この中から「あいうえお」と「ペダル」だけを検索で引っ張り出したい。というのが以下のスクリプト。


use strict;
use utf8;
use Encode;
binmode STDOUT=>’utf8’;
opendir(DIR, ’.’) || die; my @files = grep(!/^\./, readdir(DIR)); closedir(DIR);
foreach (@files){
    my $f = Encode::decode(’utf8’, $_);
    if( $f =~ m!あいうえお! ){
        print $f , " <--- hit aiueo !! \n";
    }
    elsif($f =~ m!ペダル!){
        print $f, " <--- hit pedaru !! \n";
    }
}

ところが、これでヒットするのは「あいうえお」だけで、「ペダル」はヒットしない。


twitterでの流れを追うとどうやらmacのファイルシステムが(日本語ファイル名に)使うのは NFD という形式らしい(Normalization Form Canonical Decomposition)。これをperlで判定するためには NFC という形式に変換してやる必要がある(Normalization Form Canonical Composition)


NFDとNFCで問題になるのが。

・濁音などがはいると文字コードが違ってくる。

・macの場合はさらに一般的なNFDではなくて、utf-8-mac というちょっと特殊なもの。


『Geekのメモ: Mac OS Xのファイルシステムの文字コード』

↑こちらのページが具体的でわかりやすかった(感謝!)


お馴染みの Data::Dumper で読み込んだファイル名と、スクリプト内の文字を比べると、文字コードが違っていた。まさに上記した utf-8-mac の問題。「あいうえお」は濁音が混じってないので文字コードは同じだけど、「ペダル」は濁音部分で文字コードが違っていた・文字長が増えていた。


上記サイトを参考に、CPANから Text::Iconv をダウンロード、インストールしてテストスクリプト。

1)ファイル名を utf-8-mac から、utf-8に変換する。

2)変換したファイル名にutf8フラグをつける。


use Text::Iconv;
binmode STDOUT=>’utf8’;
opendir(DIR, ’.’) || die; my @files = grep(!/^\./, readdir(DIR)); closedir(DIR);
my $h; my $n = 1;
foreach (@files){
    my $f = Text::Iconv->new(’UTF-8-Mac’, ’UTF-8’)->convert($_);
    $f = Encode::decode(’utf8’, $f);
    $h->{$f} = $n; ++$n;
}
foreach (keys %{$h}){ printf qq{%s ::: %s\n}, $_, $h->{$_}; }
print $h->{’ペダル’} ."\n";

↑で、無事確認できた。




ついでに。utf8フラグのおさらい。


文字コードのutf8とperl のutf8フラグは別モノ。


perlがスクリプトで内部処理するのにつけるのがutf8フラグということ…って、素人のわたしはよくわかってないので検証。


use strict;
use utf8;
use Encode;
binmode STDOUT=>’utf8’;
my $v = ’弱虫ペダル’;
while(<>){
    if( $_ =~ m!$v! ){ print $_; }
    my $line = Encode::decode(’ENCODE-NAME’,$_);
    if( $line =~ m!$v!){ print $_; }
}

コマンドラインで引数に渡したファイルを読み込んで、「弱虫ペダル」を含んだ行を出力(プリント)するだけの単純なスクリプト。


・スクリプト自体は文字コード、utf8で書かれている。

・「弱虫ペダル」は文字コードがutf8で、 use utf8 によって、perl の utf8フラグがついている。

・最初の if 文は、utf8フラグのついていないテキストと、utf8フラグのついた「弱虫ペダル」の比較なのでヒットしない。

・Encode::decode で読み込んだファイルのテキストにutf8フラグをつける。

 ENCODE-NAMEはファイルの文字コード。shiftjis、 utf8など。

・次の if 文は utf8フラグのついているテキストと、utf8フラグのついた「弱虫ペダル」の比較なので文字コードが * shiftjisだろうがutf8だろうが * ヒットする。


・スクリプトの文字コードはutf8でも、use utf8をつけない場合(スクリプトにutf8フラグがつかない場合)

・「弱虫ペダル」は文字コードがutf8で、perl の utf8フラグはついていない。

・最初の if 文では入力ファイルの文字コードが utf8 のものだけがヒットする。

・Encode::decode で読み込んだファイルのテキストに utf8 フラグをつける。

・次の if 文は utf8 フラグがついているテキストと、utf8フラグのついていない「弱虫ペダル」の比較なのでどれもヒットしない。


perl の utf8 フラグをつけることによって、スクリプトや、入力されるテキストの文字コードは何でも構わない、という超便利なフラグ(decodeで正しい文字コードを指定する必要はあるけど)


perlのutf8フラグネタでは以前 「utf8移行と自分メモその2」 こっちにも覚え書き(utf8フラグをつけたスクリプトは、変数に日本語が使える、というネタはこちら)


encodeネタでは変換に失敗する文字をチェックするのにencodeの第3引数に、というネタも 「メモ encodeで変換失敗を第三引数でフォロー」 …ほんと、なんちゅーかほんちゅーか。面倒くさい。



https://twitter.com/JunTajima/status/422190121455063040

話の発端からtwitterを追ってみると、utf16だったことが原因だったということでした。

て、utf16使う状況があるのか!?参考になります。ううううむ。難物。


<<2026/1>>
    123
45678910
11121314151617
18192021222324
25262728293031
検索:

【最近の20件】