use strict; use Encode; use Encode::Guess; #------- 設定 -------------------------- # チェックする文字コード(優先順) my @suspect = qw( utf8 euc-jp shiftjis ); # 文字コード用スタイル規則 my $enc_style = <<'__CSS__'; em.encoding { color:#900; font:90% Arial, sans-serif; } __CSS__ #--------------------------------------- # 推測候補を設定 Encode::Guess->set_suspects(@suspect); # XML宣言から文字コードを取得 my $xml_dec = <>; my ($out_code) = ( $xml_dec =~ m{encoding="([^"]+)"} ); $out_code ||= 'ascii'; print $xml_dec; # 1行ずつ変換処理 while (<>) { # スタイル規則を追加 s{}{\n$enc_style}; # タグの外にあり、URLエンコードを含むテキストを変換 # ※タグの途中で改行していたりするHTMLとかだとまずいです。 s{ (^|>)([^<>\n]*%[0-9A-Fa-f]{2}[^<>\n]*) } { $1 . uri_decode($2) }gex; print; } # URLエンコードを戻す sub uri_decode { my $chunk = shift; my $orig = $chunk; # デコード $chunk =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; # 文字コードの推測 my $guess = guess_encoding($chunk); # 推測できたら、そのまま内部文字列に変換 my ($result, $encoding); if (ref $guess) { $result = decode($guess, $chunk); $encoding = $guess->name; } else { # 推測できない場合、1つずつ試す foreach my $sus (@suspect) { # $guess に含まれてなければ次の候補へ next if $guess !~ /$sus/; # 内部文字列に変換 my $decoded = decode($sus, $chunk); # REPLACEMENT CHARACTER (U+FFFD)を含んでいなければそれを採用 if ($decoded !~ /\x{fffd}/) { $result = $decoded; $encoding = $sus; last; } } # それでもだめならそのまま出力 if (!$result) { $result = $orig; $encoding = $guess; } } # デコードされて出てきた & < > を変換 $result =~ s/&(?!(?:#(?:\d+|x[\dA-Fa-f]+)|[1-4A-Za-z]+);)/&/g; $result =~ s//>/g; # 結果に文字コードを付加 $result .= qq{ $encoding}; # 出力用文字コードに変換して返す return encode($out_code, $result, Encode::FB_HTMLCREF); }