今まで文字コードを意識しなくても問題なかったんで、Encode.pmってのをあまり使ってなかったんですが、
WEBからコンテンツを拾ってきて、その文字コードを判別して、一意の文字コードに変換しないといけない場面が出てきたので、
Encode.pmとかその辺について色々調べてます。そのメモ書き程度に。
WEBでの文字コード
WEB上のコンテンツは、コンテンツ毎に異なる文字コードを使う事もできます。
文字コードの宣言を明示的にしないと、文字コードの特定が難しいです。
ブラウザ見た時に文字化けをしている時は、使用している文字コードの宣言を行っていない場合や、宣言した文字コードと異なる文字コードを使っている場合などです。
文字コードの宣言がない場合に使う文字コードは、ブラウザに依存します。
プログラムでの文字コード
例えば、RSSなどで配信しているデータを取得して一覧にする、というプログラムを作るとします。
最近はどこもメジャーなBlogであればだいたいUTF-8で書かれていますが、
コレは別にRSSで配信する上で仕様で決まってるわけではなく、文字コードは任意で選べます。
一覧にするのであれば、全て同じ文字コードでないと、文字化けが発生します。
文字コードの判別方法
幸い、文字コードを宣言する仕組みがあります。
- HTTPでデータを要求した際に、ヘッダ情報にContent-Typeがあり、ここにcharsetを含める事ができます。
- HTMLであれば、<meta>タグにContent-Typeを宣言できる部分があり、ここにもcharsetを含める事ができます。
宣言がない場合での文字コードの判別
意地悪な人も世の中にはいます。その場合はどうするか。
日本語の文字コードはEUC-JP, Shift_JIS, UTF-8などが今のところメジャーですが、これらを完璧に識別する方法はありません。
でも出来る限り、識別しようとするモジュールはPerlにはあります。
Enocde::Guessです。
use Encode::Guess; my $string = 'あいうえお'; my $code = ''; my $enc = guess_encoding($string, qw/euc-jp shiftjis 7bit-jis utf8/); if(ref $enc){ $code = $enc->name; } print "$code ($enc)\n";
guess_encodingに、対象になる文字列と候補となる文字コードのリストを与えると、文字列が大体どの文字コードが使われているかがわかります。
保存する文字コードによって、得られる結果が変わります。
ref $encとするのは、guess_encodingの結果が、
文字コードを絞り込めたら、オブジェクトが、
絞り込めなかったら、文字コードの候補を文字列で返すからです。
refすると、オブジェクトなら何らかのリファレンスを示す文字が帰ってきて、
文字列なら、リファレンスではないので、偽となります。
また、ASCII文字のみだと、判別ができません(EUC-JP, Shift_JIS, UTF-8がそれぞれ扱うASCII文字のコードは全て同じだから)
その場合は別に変換しなくてもいいんですが、
日本語らしい文字が入っていても、サンプルが少ないと絞り込めない事もあります。
上の例ですと、おそらく、UTF-8, ShiftJISの時は絞り込めますが、
EUC-JPの場合は、曖昧となり絞り込めなくなるでしょう。
その場合は、コンテンツ全体を渡して意地でも特定させる等の方法があります。
ちなみに、ここで絞り込めなかったら、文字コードを判別する手段はありません。
僕は文字コードに詳しくないのですが、数百バイトものデータがあれば、大体絞り込めると思います。
絞り込めなかったらそれはASCIIコードだと決めうちすればいいんじゃないかなと思います。ASCIIコードなら変換はいりません。
文字コードがわかったら変換
Encode.pmを使って、一意の文字コードに変換できます。Encode::from_toが簡単です。
use Encode; use Encode::Guess; # 文字コードを特定し、$codeに格納する # ... Encode::from_to($string, $code => 'utf8'); print "$string\n"
Encode::from_toの挙動の注意点として、
$stringに直接変換した文字を上書きするので、
Encode::from_toを呼び出す前の$stringと後の$stringの内容は違います(変換が正常に終われば)。
Encode::encodeとEncode::decodeを使って同じ処理を書く事もできます。
$string = Encode::encode('utf8', Encode::decode($code, $string));
速度はfrom_to > decode&encodeです。
Rate encode fromto encode 62735/s -- -16% fromto 74405/s 19% --
ただ入力する文字コードが一定だとわかっている場合は、オブジェクトを作って、変換するほうが早いです。