Bulknews::Subtech RSSフィード

2007/02/13 (火)

Fix dodgy utf-8 bytes 01:44  Fix dodgy utf-8 bytes - Bulknews::Subtech を含むブックマーク はてなブックマーク -  Fix dodgy utf-8 bytes - Bulknews::Subtech

すでに utf-8 なバイト列を latin-1 と解釈して utf-8 に二重エンコードすることで起きる文字化け(を表現する短い言葉ってないのかな)を直すコード。

ちなみにPerl では Unicode 文字列と utf-8 bytes (non UTF-8 flagged) を くっつけることで発生するが、Amazon Web Services や YouTube でも頻繁におこっているみたいなので、Java/PHP なんかでも使えるかも。

use Encode;
my $re_bit = join "|", map { Encode::encode("utf-8",chr($_)) } (127..255);
sub fix_utf8 {
    my $bytes = shift;
    utf8::encode($bytes) if utf8::is_utf8($bytes);
    $bytes =~ s{(($re_bit)+)}{encode("latin-1",decode("utf-8", $1))}eg;
    $bytes;
}

方針としては 127-255 なlatin-1レンジの文字列のutf-8 bytes表現をつくり ($re_bit)、受け取った文字列にマッチさせ、utf-8 としてデコードしてコードポイントにして latin-1 にエンコードする。これで、もともと意図していた utf-8 byte 列がつくられる。

もとが latin-1 であっても動くけど、元の文字列が utf-8 であったか latin-1 であったかをコードから判定する手段はない。出来上がったバイト列を decode_utf8 するのが手っ取り早いか。

my $string = "\x{5bae}\x{5ddd}"; # 宮川
my $bytes  = encode_utf8("\x{9054}\x{5f66}"); # 達彦

my $corr = $string . $bytes; # 宮川é彦

print fix_utf8($corr); # 宮川達彦