blosxom関連の記事を辿っている時に、以下の記事を見つけました。
まず述べなければならないのは、HTMLにおける日のような文字参照の形式は
で、記事を拝見して、Unicode::Escapeも見てみたのですが、このモジュールは元々JavaScriptのUnicodeエスケープ形式を扱うものであるようで、それを更にHTMLの数値文字参照変換のために使うというのは、ややオーバースペックなような気がしました。
答えの1つは、記事が参照していたはてなでの質問でも既に述べられています。
$str = '日本語文字列'; $str =~ s/(.)/'&#'.ord($1).';'/eg; print $str;
ただ、この例だとやや不十分で、$strは生のUTF-8文字列でなく、Perlの内部文字列(UTF8フラグつきの文字列)に変換しておく必要があります。そうすることにより正規表現の .
が、バイトごとにではなく、Unicode文字ごとにマッチするようになります。
※ 以下に挙げる例は、UnicodeをサポートしたPerl(Perl 5.8以降)を対象とするものとします。
ソースに書かれたUTF-8文字列であれば、use utf8;
するだけで内部文字列として扱われます。
use utf8;
$str = '日本語文字列のtest';
$str =~ s/(.)/'&#'.ord($1).';'/eg;
print $str, "\n";
または、utf8::decode
関数を使うとか。(参照: utf8::* 関数)
$str = '日本語文字列のtest';
utf8::decode($str);
$str =~ s/(.)/'&#'.ord($1).';'/eg;
print $str, "\n";
入力がUTF-8以外の文字コードであれば、Encodeモジュールのdecode
を使う方法もあります。
use Encode;
$str = decode('shiftjis', $sjis);
$str =~ s/(.)/'&#'.ord($1).';'/eg;
print $str, "\n";
ちなみに十六進の形式にするなら、こんな感じでしょうか。
use utf8;
$str = '日本語文字列のtest';
$str =~ s/(.)/sprintf '&#x%x;', ord($1)/eg;
print $str, "\n";
これとは別に、Encodeモジュールの機能を使う方法もあります。Encodeモジュールでは、例えば変換しようとする文字列に、変換先の文字エンコーディングで扱えない文字が含まれていた場合、その文字を十進の数値文字参照・十六進の数値文字参照・Perlの文字列エスケープ形式に変換することができます。(参照: Handling Malformed Data)
例えばこんな風に。
# charref.pl
use strict;
use Encode qw(encode decode :fallbacks);
my $utf8 = '日本語文字列のtest';
# 内部形式に変換
my $internal = decode('utf8', $utf8);
# 十進数値文字参照
my $dref = encode('ascii', $internal, FB_HTMLCREF);
# 十六進数値文字参照
my $xref = encode('ascii', $internal, FB_XMLCREF);
# Perl文字列エスケープ
my $pqq = encode('ascii', $internal, FB_PERLQQ);
print $dref, "\n";
print $xref, "\n";
print $pqq, "\n";
$ perl charref.pl 日本語文字列のtest 日本語文字列のtest \x{65e5}\x{672c}\x{8a9e}\x{6587}\x{5b57}\x{5217}\x{306e}test
上記では、UTF-8文字列をむりやりASCIIに変換することにより、ASCII文字以外の文字のエスケープを実現しています(なのでASCII文字はそのまま残ります)。