daily dayflower

2009-01-29

use utf8 環境下で => オペレータの左辺が UTF8 flag on になってしまう

use strict;
use warnings;

sub Dump {
    @_ = map {
             sprintf "'%s'(%s)", $_, utf8::is_utf8($_) ? 'utf8' : 'bytes'
         } @_;

    print {*STDERR} join(q{, }, @_), "\n";
}

no utf8;

Dump( foo => 'bar' );
#=> 'foo'(bytes), 'bar'(bytes)

no utf8; だもんで,両者とも bytes なのは,まぁあたりまえ。


ところが,これを use utf8; で動かすと……

use utf8;

Dump( foo => 'bar' );
#=> 'foo'(utf8), 'bar'(bytes)

foo の UTF8 flag が on になってる!


use utf8 にしてたので,任意の文字列リテラルが UTF8 flag on になっても文句はいえないです。しかし,一般の文字列リテラルでは,Latin-1 の character set に収まっている場合,UTF8 flag は on になりません。実際 'bar' は Latin-1 に収まっているので UTF8 flag は off です。

ということで,回避方法?ですが,=> オペレータの左辺を,きちんとした文字列様式(つまりクォーテーションで囲うなど)にすると

use utf8;

Dump( 'foo' => 'bar' );
#=> 'foo'(bytes), 'bar'(bytes)

両者とも bytes になります。

にて確認しました。

仕様といえば仕様?バグ

2009-01-29 追記

=> に限らなかったです。なんというか,生の(文字列とみなされる)リテラルならそうなる感じ?

use utf8;

my %h;
$h{foo} = 'bar';
$h{'baz'} = 'ban';

Dump(%h);
#=> 'baz'(bytes), 'ban'(bytes), 'foo'(utf8), 'bar'(bytes)

my $d = <<'END_DOC'
foo
END_DOC
chomp $d;

Dump($d)
#=> 'foo'(utf8)

ヒアドキュメントはまぁ仕方ないですけど,それ以外のケースは,なんというか直感に反するというかなんというか。

どこで困った?

URI::query_form() ではまりました。

use URI;

my $uri = URI->new('http://example.com/');

use utf8;

$uri->query_form( bytes => "\x{a4}" );
print $uri, "\n";
#=> http://example.com/?bytes=%C2%A4

use utf8 してたから "\x{a4}"U+00A4 としてみなされたんだろうなぁ*1,しかたねえなぁと思って別の API をたたいてみたら,

$uri->query( "bytes=\x{a4}" );
print $uri, "\n";
#=> http://example.com/?bytes=%A4

きちんとエンコードできるでないか。なぜだなぜだ。

と思って調べたら,先ほどの現象をみつけたわけです。

先ほどと同じように

$uri->query_form( 'bytes' => "\x{a4}" );
print $uri, "\n";
#=> http://example.com/?bytes=%A4

とすれば回避できます。


URI::query_form()

                push(@query, "$key=$val");

というところで,たとえ $val が UTF8 flag off だったとしても $key が(=> の左辺などで)UTF8 flag on だった場合,全体が UTF8 flag on に upgrade してしまうんですね。んでその場合,UTF8::Escape::escape_char() が UTF8 flag on 文字列を UTF8 octets に convert してしまうので上記のような変なエスケープになっているわけです。

*1:ほんとはここ誤解です。utf8::is_utf8("\x{a4}") しても off です。

miyagawamiyagawa 2009/08/27 14:56 左辺が ascii のみにもかかわらずフラグがつく問題はレポートしたところblead で修正されました。 http://rt.perl.org/rt3/Public/Bug/Display.html?id=68812

dayflowerdayflower 2009/09/18 16:03 ありがとうございます。

仕様といわれれば仕様かもしれんと思ってほっておいた自分を恥じることにします。てか,本体も RT ってあったんですね。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証