- ベストアンサー
perl5.8.3のunicode環境で日本語の置換え
お助けください。m(__)m perl5.8.3環境で、utf-8にて書かれたperl内での日本語置換えの質問です。 テキストファイルを「abc.dat」、処理するperlファイルを「rep.cgi」とします。 abc.datを読み込み、ファイル内の文字列「abc」を「あいう」に置換えする部分を「$rep =~ s/abc/あいう/;」とすると、置換えはうまくいっているようなのですが、abc.datに書かれた置換え以降の日本語が文字化けします。 原因はいったいどこにあるのでしょう?? 【テキストファイル abc.dat】文字コードUTF-8、改行コードLF <div> <p>abc</p> <p>あいう</p> </div> 【perlファイル rep.cgi】文字コードUTF-8、改行コードLF use utf8; use Encode; open(TMPL,"<:encoding(utf8)","abc.dat") || die('Error'); while (<TMPL>) { $rep .= $_; } close(TMPL) || die('Error'); $str = 'あいう'; $rep =~ s/abc/$str/; print $rep; 【結果】 <div> <p>あいう</p> <p>ããã</p> </div>
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
#4>のところで、$repにutf-8フラグが立っていませんでした。 調べてみたんですが、(ウチの環境は、5.8.7) open(TMPL,"<:encoding(utf8)","abc.dat") || die('Error'); while (<TMPL>) { print "on Flag:$_\n" if(Encode::is_utf8($_, true)); $rep .= $_; } close(TMPL) || die('Error'); のようにして調べたんですが、 この場合、フラグは、立ってますね。('on Flag' がprint される) >open(TMPL,"<:encoding(utf8)","abc.dat") || die('Error'); を open(TMPL,"<","abc.dat") || die('Error'); にしてみて実行すると、'on Flag' がprint されないので テストが正しいと思われます。(つまり、#4の場合フラグが立っているか立っていないかというと、立っている) もしかすると、おっしゃるようにバージョンの問題なのかもしれませんが、(5.8.3ではテストしていません) perldoc で、Encode::is_utf8 の説明を読むと 5.8.1 以降同じ動作というように思えますので、 何かの勘違いではないでしょうか 私の勘違いだったらすみません
その他の回答 (4)
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
>ところでutf8::encodeってなんでしょ? perldoc utf8 してみたらわかるよ >utf8::encode(文字)でutf8フラグを外し、再度$value = decode('utf8',$value)とすると結果は?????となってしまいます urlencoding は、生UTF-8なんで、encodeでフラグをはずすというのは、見当違いです。 逆に、decode してフラグを付けてやる(内部表現にしてやる)といいかと、 いったん内部表現にすれば、あとは、出力レイヤーでもなんでも、うまくいきます。
お礼
BLUEPIXY様、たびたびのご返答とutf8::encodeの補足までありがとうございます。 うっかりしておりましたm(__)m utf8::encodeは、対象のバイト列のUTF8フラグを落とす(UTF8文字列を同じ内部表現のままバイト列に変換する)関数と覚えております。参考 http://naoya.dyndns.org/~naoya/mt/archives/000611.html BLUEPIXY様のおっしゃるように、デコード前の文字列に対してフラグをはずすというのは見当違いでした。 お恥ずかしい・・・ 原因は、 open(TMPL,"<:encoding(utf8)","abc.dat") || die('Error'); while (<TMPL>) { $rep .= $_; } close(TMPL) || die('Error'); のところで、$repにutf-8フラグが立っていませんでした。 他の箇所で配列にpushしているところは動作しているようでしたので、 while (<TMPL>) { push(@rep,$_); }にすることで解決しました。 perl5.8.0では、openで読み込んだものを.=で変数に入れても動作していたのですが、perl5.8.3のみの現象なのでしょうか?
- 11th_style
- ベストアンサー率50% (45/90)
No.2さんと同意見で、use encodingはあまりお勧めしません。影響範囲がでか過ぎます。use utf8がいいと思います。 で、perlが5.8.6で現象が再現しないので確定ではないのですが、確か、昔のperlだとシングルクォートの文字列にはuse utf8をしてもunicodeフラグが立たなかったことがあったような気がします。 $str = "あいう"; と、ダブルクォートでかこってみてはどうでしょう??
お礼
11th_style様、ご返答ありがとうございます。 use encodingだとモジュールなどにまで影響してしまうとのことでしたので、11th_style様のご指摘どおり、当初のuse utf8で処理したいと思います。 そのうえでお教えいただいたダブルクォートに変更してみたのですが、やはりabc.dat内の置換えた以降の日本語部分のみが文字化けします。 utf-8フラグが関係している・・・ということなんでしょうか?
- Hasty
- ベストアンサー率73% (19/26)
単に出力レイヤを指定してないのが原因では? binmode STDOUT, ":encoding(utf8)"; しておけばOKのはず。 ファイル出力の場合は出力前に binmode FH, ":encoding(utf8)"; それと >s/%([0-9A-Fa-f][0-9A-Fa-f])/pack("C",hex($1))/eg;したものをutf8::encode(文字)で utf8フラグを外し、再度$value = decode('utf8',$value)とすると結果は?????となってしまいます。 は、s/%([0-9A-Fa-f][0-9A-Fa-f])/pack("C",hex($1))/eg;したものにutf8フラグがついていると考えるのが間違いで、encodeする必要はないです。 ところでutf8::encodeってなんでしょ? encode("utf8",~)かencode_utf8(~)の間違いでは?? ちなみにencodingプラグマは適用される範囲がブロックやファイル単位じゃなくスクリプト全体になっちゃうんで、使えなくなるモジュールがあるとかmod_perlでは他のスクリプトにまで影響が出るとかいろいろ問題があって、ちゃんと理解してないのであれば使わない方がいいです。
お礼
Hasty様、ご返答ありがとうございます。 色々調べながら変更していったのですが、少し混乱していて下のコードがおかしなことになってますね。。 use encodingはスクリプト全体へ影響してしまうんですね。 モジュールのutf8対応状況まで考慮している時間がないので、今回はご指摘どおり、当初のuse utf8で処理したいと思います。 ありがとうございました。
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
use utf8; use Encode; を use encoding "UTF-8"; に変えてみたらどうでしょう
お礼
BLUEPIXY様、お返事が遅れてすみません。 ご返答感謝いたします。 おかげ様で、上に書いた処理は正常に置換えされましたが、今度はuse encoding時のデコードでハマっています。 $strの文字をフォームから受け取る形にしたいのですが、http://www.pure.ne.jp/~learner/program/Perl_unicode.htmlによるとuse encodingした場合、「PerlIOレイヤの STDIN と、STDOUT を指定された CHAR_SET にする。」とあります。 s/%([0-9A-Fa-f][0-9A-Fa-f])/pack("C",hex($1))/eg;したものをutf8::encode(文字)でutf8フラグを外し、再度$value = decode('utf8',$value)とすると結果は?????となってしまいます。 use encoding時のデコード方法はどのようにすればよいのでしょうか? use strict; use encoding 'UTF-8'; use Encode qw/ encode decode /; my %POST = decode_Str(); if (exists($POST{'moji'})) { comp_Rep(%POST); } else { disp_Form(); } sub disp_Form { print <<"EOM"; Content-type:text/html; charset=utf-8 <form method="post"> <input type="text" name="moji" /><br /> <input type="submit" value="送信" /> </form> EOM exit; } sub comp_Rep { my %POST = @_; my $str = $POST{'moji'}; print $str; my $rep; open(TMPL,"<:encoding(utf8)","abc.dat") || die('Error'); while (<TMPL>) { $rep .= $_; } close(TMPL) || die('Error'); $rep =~ s/_%abc%_/$str/; print $rep; exit; } sub decode_Str { if ($ENV{'CONTENT_TYPE'} =~ /application\/x-www-form-urlencoded/i) { read(STDIN,$buffer,$ENV{'CONTENT_LENGTH'}); @data = split(/&/,$buffer); foreach $buffer (@data) { ($key,$value) = split(/=/,$buffer); $value =~ tr/+/ /; $value =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack("C",hex($1))/eg; utf8::encode($value); $value = decode('utf8',$value); } return %POST; } } }
お礼
BLUEPIXY様。 テストまでしていただいて本当に感謝です。 おかげ様でなんとか動作させることが出来ました。 質問に書いた簡易的なテストプログラムは削除しなくてはならなかったため、再度コピペして再現しようと思ったら問題なく動作しました・・・何故??? とにかくNo.4に書いたような「.=」が原因ではないということですね。 ひとまず問題解決しましたので締め切らせていただきます。 稚拙な質問にお付き合いいただきありがとうございました。