camel

ちょうどいい機会なので、Perl 5.8以降におけるutf8フラグの立ち方を。

unknownplace.org - 2008/02/17 - utf8::is_utf8
ということで、"\x{6751}\x{702c}\x{5927}\x{8f14}" などというData::Dumper表記でかならずしも utf-8フラグがたつわけじゃない。ということがいいたかったんだと思うのだけれど、

\x{UUUUUU}とutf8 flag

まずはクイズです。以下がどう出力されるかを答えなさい。

sub pfrag{ print utf8::is_utf8($_[0]) ? 1 : 0, "\n" }
pfrag "Hell\xC3, world!";
pfrag "Hell\x{C3}, world!";
pfrag "Hell\x{FFC3}, world!";

答えは、「0,0,1と一行ずつ出力される」です。これはuse utf8プラグマとは関係なく常にそうなります。

それでは、Perlはどこを見てフラグを切り替えているのでしょう?\x{UUUUUU}の中身です。UUUUUUが、0x100以上の場合のものが一つでも含まれていればフラグを立て、そうでなければ立てません。

直書きとutf8 flag

それでは、以下の場合はどうでしょうか。

sub pfrag{ print utf8::is_utf8($_[0]) ? 1 : 0, "\n" }
no utf8;
{
   use utf8;
   pfrag "弾";
   pfrag "\x{c3}弾";
}
{
  pfrag "弾";
  pfrag "\x{c3}弾";
}

答えは、「1,1,0,0と一行ずつ出力される」です。この場合、use utf8プラグマの有無が振るまいを変えます。

以上をpseudocodeでまとめると、以下のとおりとなります。

if (引用符の中に(エスケープも含めて)ascii以外の文字が入っている?){
  utf8flag = utf8プラグマ;
}else{
  if (引用符の中に\x{}や\N{}などのエスケープが含まれている){
    utf8flag = ord(\x{}) >= 0x100 が含まれている? 1 : 0;
  }
  else{
    utf8flag = 0;
  }
}

なぜこうなっているかといえば、下位互換性。"Perlの文字リテラルはISO-LATIN-1である"という旧来の常識で書かれたコードがあまりに多かったのでこういう形になりました。

問答無用にutf8フラグを立てる

それでは、問答無用にutf8フラグを立てるにはどうしたらよいでしょうか。今のところの公式の推奨は

use Encode;
my $utf8 = decode_utf8($unknown);

となります。Perl 5.8.1からは

utf8::decode($unknown);

も使えるようになりましたが、しかし後者の場合は引数が定数の場合は使えません。フラグの変更も書き込みに相当するからです。前者の方がベタープラクティスです。その逆、問答無用でフラグを落とすには、

use Encode;
my $bytes = encode_utf8($unknown);

とします。

Dan the Encode Maintainer