[Perl] trで全角・半角変換しようとしたらはまる

メモ。

perlのtrはコンパイル時に変数展開するので、動的に置き換える文字を変更する場合、evalを使うことになる。
おじいさんは、これはいまいちだと思うんだがね。

 eval "tr/$from/$to/";

みたいな感じ。
さて、ここで全角→半角変換したいと思うので、$fromに"0-9"、$toに"0-9"を入れるとする。

ここから、動きがよくわからない。
"tr/0-9/0-9/"の箇所の全角は、utf8フラグがついているか?

$fromにフラグがついていれば、evalに引き渡す文字全体はutf8フラグがついているのはわかる。
しかし、ソースコードとして解釈される"0-9"にutf8フラグがついているかが、ここだけではわからないんじゃないか。

というのは、$_、$from、 $toにフラグがついていて、"1"が変換されなかったケースがあった。mod_perl上だけれど。
しかも、ループの中でこのロジックを呼び出している(subsubな奥であるが)とき、3つの文字列の置換のうち最後の1つだけ失敗するという意味不明な状態。

Devel::PeekのDumpで各変数をみても、まったく同じだが、最後だけ失敗。
単体で動かすと、動作する。
わけがわからん。

結局、

 eval "use utf8; tr/$from/$to/"

としたら、謎の動作は収まった。このソースコードのスコープについて、ASCII範囲外の文字はutf8フラグをつけるという事を強制するので、それで収まったんだと思う。そういう所から、原因を逆算していると、こういう事ではないか、という所だ。
本来は、Bモジュールなんぞを使って解析すべきかもしれんかったが、そこまで思いつかなかった。

だが、まあ、こんなtrするよりも、Encodeの半角全角変換を使った方がよいのではないかと思う。

ブログ気持玉

クリックして気持ちを伝えよう!

ログインしてクリックすれば、自分のブログへのリンクが付きます。

→ログインへ

なるほど(納得、参考になった、ヘー)
驚いた
面白い
ナイス
ガッツ(がんばれ!)
かわいい

気持玉数 : 0

この記事へのコメント

kinsan
2012年05月27日 07:29
trでも、s///やm//と同様に、evalのオプション(e)が使えませんか?
2012年05月29日 00:27
ご指摘ありがとうございます。s///の場合ならば、%a=('1'=>1); s/[0-9]/$a{$1}/eのように(%aの中身は省略してますが)、ハッシュで置き換え先の文字をevalで展開すればいいわけなんですが、trではevalオプションはないので、この戦略は今回は使えなかったんですよ。使えれば楽だったんですけれどねえ。

この記事へのトラックバック