binmode関数を2回繰り返すと結果が狂う?

このQ&Aのポイント
  • Perlのbinmode関数を2回繰り返すと結果が狂う現象について質問です。
  • binmode関数を使ってエンコードを設定し、テキストを表示する際に2回同じコードを書くと結果が異なる現象に遭遇しました。
  • どうしてエンコードを2回設定すると結果が変わるのか、また、表示される文字列のダンプがどうやって得られるのかについての解説をお願いします。
回答を見る
  • ベストアンサー

binmode関数を2回繰り返すと結果が狂う?

お世話になります。 「まるごとPerl」という雑誌の記事をWindowsに移植してEncodeモジュールの勉強中です。 #! perl # list1 -- 日本語の文字を1文字として解釈 use strict; use warnings; use utf8; # スクリプトは UTF-8 で保存 binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更 #4文字目のカから4文字、カタカナと表示される my $text = '漢字、カタカナ、ひらがなの混じったtext'; print substr($text, 3, 4); というスクリプトをUTF-8で保存してWindowsのDOS窓で(コードページはデフォルトのCP932のままで)実行すると C:\> list1.pl カタカナ と無事表示されます。 ところが、うっかり間違えて binmode を binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更 binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更 と2回書いてしまうと、結果がこうなります。 C:\> list1.pl "\x{00ca}" does not map to cp932. "\x{00de}" does not map to cp932. "\x{00ca}" does not map to cp932. "\x{00e9}" does not map to cp932. \x{00ca}\x{00de}\x{00ca}\x{00e9} これはいったい何が起こっているのでしょうか。 binmode はあるファイルハンドルのエンコードを決めるもので、何回やっても結果が一緒なのかと思っていました・・・。 00ca、00de、00ca、00deというのも、CP932の「カタカナ」のダンプ「834A 835E 834A 8369」をどうすれば得られるのか不明です・・・。 よろしくお願いします!!!

  • Perl
  • 回答数1
  • ありがとう数1

質問者が選んだベストアンサー

  • ベストアンサー
  • Gotthold
  • ベストアンサー率47% (396/832)
回答No.1

たぶん、UTF8→CP932変換が二回実行されています。 00caは、834AをUTF8と見なしたときのコードポイントだと思います。 (834AはUTF8としては不正なオクテットシーケンスだけど、 細かいところ無視すればUTF8のU+00CAと解釈はできそう。)

TYWalker
質問者

お礼

なるほど!!! バッチリわかりました。 パイプのように効くんですね。 ちなみに binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更 binmode STDOUT, ':utf8'; # 出力はUTF-8に変更 binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更 でうまく行くかと思いきやダメでした ;;; ありがとうございました!!!

関連するQ&A

  • WindowsでUTF-8のPerlスクリプトを実行する場合。。。

    お世話になります。 2006年に発行された「まるごとPerl」というムックの「まるごとEncode」という記事を見て Perl の日本語処理の勉強をしています。 本記事は Perl5.8 対象と思われますが私は Strawberry Perl 5.10.0 を Windows XP SP3 で使っています。 #! perl # list1 -- 日本語の文字を1文字として解釈(UTF-8で保存) use strict; use warnings; use utf8; my $text = '漢字、カタカナ、ひらがなの混じったtext'; print substr($text, 3, 4); というサンプルコードがあって、実行結果は「カタカナ」になると書いてあります。 要するに substr 関数が use utf8; によってバイト単位ではなくて文字単位で効くようになるので、4文字目から4文字が正しく取ってこられるという実験ですが、Windows の DOS 窓で実験すると、当然端末コードは Shift_JIS というか CP932 ですので、 Wide character in print at C:\Marugoto\list1.pl line 8. 繧ォ繧ソ繧ォ繝 となります。 そこで、上のコードに binmode 関数を足して、 #! perl # list1 -- 日本語の文字を1文字として解釈(UTF-8で保存) use strict; use warnings; use utf8; binmode STDOUT, ':encoding(cp932)'; # 追加 my $text = '漢字、カタカナ、ひらがなの混じったtext'; print substr($text, 3, 4); とすると正しく動作しました。 次に、 #! perl # list2 -- 1オクテットを1文字として解釈(UTF-8で保存) use strict; use warnings; my $text = '漢字、カタカナ、ひらがなの混じったtext'; print substr($text, 9, 12); というリストがあって、今度は use utf8; がないのでバイトモードで使う(substr 関数は 9 バイト目から 12 バイトを取ってくる。UTF-8 は全角日本語文字が1文字3バイトなので)という実験ですが、これもそのまま DOS 窓で実行すると 繧ォ繧ソ繧ォ繝 となるので、さっきと同じように binmode 関数を使って、 #! perl # list2 -- 1オクテットを1文字として解釈(UTF-8で保存) use strict; use warnings; binmode STDOUT, ':encoding(cp932)'; # 追加 my $text = '漢字、カタカナ、ひらがなの混じったtext'; print substr($text, 9, 12); と書いたのですが、 ・\x{0082}" does not map to cp932. "\x{0082}" does not map to cp932. "\x{00bf}" does not map to cp932. "\x{0082}" does not map to cp932. "\x{0083}" does not map to cp932. "\x{008a}" does not map to cp932. a\x{0082}≪a\x{0082}\x{00bf}a\x{0082}≪a\x{0083}\x{008a} のようになりました。 もちろん、プログラムを Shift_JIS で保存して substr($text, 6, 8) とすれば正しく実行できますが、プログラムを UTF-8 で保存しつつ、Windows で正しく表示する方法はあるでしょうか。 よろしくお願いいたします。

    • ベストアンサー
    • Perl
  • {ブロック}の外でのみ use utf8; したいのだが・・・

    連投申し訳ありません。 「まるごとPerl」(2006年9月、インプレス刊)という本の「まるごとEncode」という記事に従ってEncodeの勉強をしています。 原記事は UNIX(というか端末コードをUTF-8に出来る環境)ですが、それをWindowsに移植しようとして苦労しています。 #! perl # list3 -- UTF-8モードとバイトモードの切り替え #      インデントを表現するために全角空白を使っています use strict; use warnings; binmode STDOUT, ':encoding(cp932)'; # 追加 # ブロックの外側ではUTF-8文字単位で解釈される use utf8; {  # ブロックの外側ではバイトモードが強制される  use bytes;  my $text = '漢字、カタカナ、ひらがなの混じったtext';  print Encode::decode('UTF-8', substr($text, 9, 12)); # カタカナと表示されたい } my $text = '漢字、カタカナ、ひらがなの混じったtext'; print substr($text, 3, 4); # カタカナと表示されたい __END__ というプログラムを実行すると C:\>list3.pl カタカナカタカナ と表示されてほしいのですが、 C:\>list3.pl Cannot decode string with wide characters at C:/strawberry/perl/lib/Encode.pm li ne 174. と表示されます。 use utf8 を後ろにズラして #! perl # list3 -- UTF-8モードとバイトモードの切り替え #      インデントを表現するために全角空白を使っています use strict; use warnings; binmode STDOUT, ':encoding(cp932)'; # 追加 {  # ブロックの外側ではバイトモードが強制される  use bytes;  my $text = '漢字、カタカナ、ひらがなの混じったtext';  print Encode::decode('UTF-8', substr($text, 9, 12)); # カタカナと表示されたい } # ブロックの外側ではUTF-8文字単位で解釈される use utf8; # 移動 my $text = '漢字、カタカナ、ひらがなの混じったtext'; print substr($text, 3, 4); # カタカナと表示されたい __END__ とするとうまく動いて C:\>list3.pl カタカナカタカナ と表示されます。 #! perl # list3 -- UTF-8モードとバイトモードの切り替え #      インデントを表現するために全角空白を使っています use strict; use warnings; binmode STDOUT, ':encoding(cp932)'; # 追加 # ブロックの外側ではUTF-8文字単位で解釈される use utf8; my $text = '漢字、カタカナ、ひらがなの混じったtext'; # 移動 print substr($text, 3, 4); # カタカナと表示されたい # 移動 {  # ブロックの外側ではバイトモードが強制される  use bytes;  my $text = '漢字、カタカナ、ひらがなの混じったtext';  print Encode::decode('UTF-8', substr($text, 9, 12)); # カタカナと表示されたい } __END__ のようにするとやはり最初と同じエラーになりますが、 #! perl # list3 -- UTF-8モードとバイトモードの切り替え #      インデントを表現するために全角空白を使っています use strict; use warnings; binmode STDOUT, ':encoding(cp932)'; # 追加 # ブロックの外側ではUTF-8文字単位で解釈される use utf8; my $text = '漢字、カタカナ、ひらがなの混じったtext'; # 移動 print substr($text, 3, 4); # カタカナと表示されたい # 移動 no utf8; # 追加 {  # ブロックの外側ではバイトモードが強制される  use bytes;  my $text = '漢字、カタカナ、ひらがなの混じったtext';  print Encode::decode('UTF-8', substr($text, 9, 12)); # カタカナと表示されたい } __END__ だと大丈夫です。 結果として、ブロックの外で use utf8;、中では use bytes; という記事の著者の意図通りに動作しないようで、ブロックの中まで use utf8; が効いているようです・・・。 これは Perl の実装が変わったのでしょうか。 使用しているのは Windows XP Home SP3+Strawberry Perl v5.10.0 です。

    • ベストアンサー
    • Perl
  • does not map to shiftjis は解決不可能でしょうか?

    日本語処理、ActivePerlでは無理なのでしょうか? 下記test.plを実行すると、 ======================================================== "\x{00e3}" does not map to shiftjis at test.pl line 10. ... u is \x{00e3}\x{0082}\x ... "\x{0082}" does not map to shiftjis at test.pl line 11. ... s is \x{0082}\x{00e6}\x{0081}[\x{0082}± me is 倉田真由美 ========================================================= となって、倉田真由美しか期待通りに出力されません。 外部ファイルの"よーこ"を正しく扱う方法はないのでしょうか? test.pl (utf-8) -------------------------------------------- use utf8; binmode STDOUT => ":encoding(shiftjis)"; binmode STDERR => ":encoding(shiftjis)"; require 'u.pl'; # utf-8 の外部スクリプト require 's.pl'; # sjisの外部スクリプト $me = "倉田真由美"; print "u is $u\n"; print "s is $s\n"; print "me is $me\n"; ------------------------------------------- u.pl (utf-8) ------------------------------------------- $u = "よーこ"; ------------------------------------------- s.pl (sjis) ------------------------------------------- $s = "よーこ"; ------------------------------------------- ActivePerl 5.8.0.806 Windows2000 (cmd.exe) よろしくお願い申し上げます。

    • ベストアンサー
    • Perl
  • Data::Dumper;でダンプ後表示した文字列\x{30fc}...

    お世話になります。 下記のスクリプトを使ってrssファイル内をuse Data::Dumper;でダンプして表示してみたのですが、rssファイルがutf8で記述されているせいか 表示時に日本語の所が\x{30fc}のような文字列になってしまいます。 これをshiftjisで表示させたいのですが、試行錯誤してもうまくいきません。 どなたかご教授いただけませんでしょうか。 #!/usr/bin/perl BEGIN{ $| = 1; print "Content-type: text/html\n\n"; open(STDERR, ">&STDOUT"); } use XML::RSS; use Data::Dumper; my $rss = new XML::RSS; #表示形式の違いで1と2がある。 $Data::Dumper::Indent = 1; use open IN => ":utf8"; # 入力をUTF8とする use open OUT => ":shiftjis"; #use open ":std"; #use Encode; #use encoding 'shiftjis', STDIN=>'utf8', STDOUT=>'sjis'; #binmode STDOUT, ":encoding(utf-8)"; #binmode STDOUT, ":encoding(shiftjis)"; #binmode STDOUT, ":encoding(euc-jp)"; #use open ":encoding(shiftjis)"; # rssをセット。 open my $fh, '< ./test.rss'; my $text = join undef, <$fh>; close $fh; # rssをパース $rss->parse($text); # ひとまず中見を知る為にダンプしてみる print "Content-type:text/html;charset=Shift_JIS\n\n"; print "<html><head>\n"; print "<title></title></head>\n"; print "<body>\n"; print Data::Dumper->Dump([$rss]);

    • ベストアンサー
    • Perl
  • HTTP::Request::Common qw(POST);時にソフト表だとエラーになる

    お世話になります。 HTTP::Request::Common qw(POST);を使ってPOSTする際、aaa=> 'あいう',などの日本語はPOST後、データが渡っている事が確認できるのですが、'ソフト表'等の文字列をあえて送ろうとすると、エラーになってしまいます。 記述はShift_JISで行っています。 先生方、ご教授ねがえませんでしょうか。 #!/usr/bin/perl BEGIN{ $| = 1; print "Content-type: text/html\n\n"; open(STDERR, ">&STDOUT"); } use HTTP::Request::Common qw(POST); use LWP::UserAgent; #use open IN => ":utf8"; # 入力をUTF8とする #use open OUT => ":shiftjis"; #use open ":std"; #use Encode; use encoding 'shiftjis', STDIN=>'utf8', STDOUT=>'shiftjis'; #binmode STDOUT, ":encoding(utf-8)"; #binmode STDOUT, ":encoding(shiftjis)"; #use open ":encoding(shiftjis)"; #require './jcode.pl'; my $ua = LWP::UserAgent->new; #タイムアウトを設定 $ua->timeout(10); my $req1 = POST 'http://domain.com/test.cgi', [ aaa => 'www1', bbb => "ソフト表" ,#bbb=> 'あいう',だとエラーになりません ]; print $ua->request($req1)->as_string;

    • ベストアンサー
    • Perl
  • perlで辞書データを置換

    perl超初心者です。 初めて組んで行き詰りました。 辞書データから不必要なものを取ろうと 思いましたが、いきなり[ファイルを開く] で止まってます。 以下がそうです。 use utf8; binmode STDIN, ":encoding(cp932)"; binmode STDOUT, ":encoding(cp932)"; open(DIC, "+<", "pdic2csv.csv") or die("Error:$!"); @array = <DIC>; $a = ~ s|" / "|"_"|g; @array = ("\,","\\","\s",1,2,3); $a = ~ s/1,2,3/"_"/g; $a = ~ s|" /// "|"\,"|g; close(DIC) ----------------- 使う辞書データは以下です。 うつる /// 건너다 / [綴り]ㄱㅓㄴㄴㅓㄷㅏ うつわ /// 그릇 / 그릇は,(~)杯, \ cf. 잔 うで /// 팔 よろしくお願いします。

  • <STDIN>とARGVとの振る舞いの違いが分からず困っております。

    <STDIN>とARGVとの振る舞いの違いが分からず困っております。 Perlといいますか、プログラム初心者です。 現在、Perlでファイル処理を行おうと思っており、下記のようなスクリプトを書き、file_test.plとして保存・実行しようとしたのですが、実行時の命令の仕方により処理が出来たり出来なかったりで、困っております。どなたか、ご教授頂けませんでしょうか。 なお、使用環境はWindows XP, Active Perl 5.8.8です。 スクリプトのソース、読み込みファイル共にShift-JISです。 (ここには記載しておりませんが、処理後の吐き出しファイルはUTF-8になります) -------------------- file_test.plのソースファイル -------------------- use strict; use warnings; use encoding "cp932"; use Encode::Guess qw/euc-jp shiftjis 7bit-jis/; binmode STDIN, ':encoding(cp932)'; binmode STDOUT, ':encoding(cp932)'; binmode STDERR, ':encoding(cp932)'; my $csvname; if(defined($ARGV[0])){ $csvname = $ARGV[0]; print "csvname = $csvname\n"; #文字コード判定 my $enc = guess_encoding($csvname); ref($enc) or die "Can't guess: $enc"; print "encoding is ", $enc->name, "\n"; #UTF-8フラグチェック print utf8::is_utf8("$csvname") ? "UTF-8 Flag\n" : "not UTF-8 Flag\n"; } else{ print "input file: "; $csvname = <STDIN>; chomp($csvname); print "csvname = $csvname\n"; #文字コード判定 my $enc = guess_encoding($csvname); ref($enc) or die "Can't guess: $enc"; print "encoding is ", $enc->name, "\n"; #UTF-8フラグチェック print utf8::is_utf8("$csvname") ? "UTF-8 Flag\n" : "not UTF-8 Flag\n"; } open CSV, '<:encoding(sjis)', $csvname or die "$!"; print "FILE OPEN OK\n"; close(CSV); ------------------- file_test.plのソースファイル END ------------------- このソースを"perl file_test.pl csvfile.csv" で実行すると問題なく処理できます。 しかし、"perl file_test.pl" で実行し、あとからファイル名を入力すると、 "Invalid argument at file_test.pl line 35, <STDIN> line 1."というエラーが出ます。 (35行目はOPENコマンドの行です) しかし、 "perl -CA file_test.pl"と-CAオプションを入れ、後からファイル名を入力した場合、動作します(上述のファイルの実行と同時にファイル名を指定したものでも動きます)。 実際にはコマンドプロンプトからの実行ではなく、plファイルをダブルクリック、その後ファイル名を指定して処理を行いたいため、悩んでおります。 どのようにしたら、うまく実行できるか、ご教授頂ければ幸いです。

  • Encode と encoding の同時使用で ISO-2022-JP に encode できない

    CentOS を 5.1 から 5.2 にアップデートした頃から PerlCGI からのメール送信が出来なくなって、調べていたら「ISO-2022-JP への encode がおかいぞ問題」に辿り着きました。 以下のコードで、euc-jp が吐かれてしまいます。 #! /usr/bin/perl -w use encoding('UTF8'); use Encode; binmode(STDOUT); my $text = "<全角文字ですよぉ。>"; print encode('ISO-2022-JP', $text), "\n"; 以下のいずれかで正常に jisコードを吐く様になるのですが、こんなものなんでしょうか? 1 「use encoding('UTF8');」 を 「use utf8;」に替える 2 print の直前に "no encoding;" を入れる CentOS 5.1 では多分正常に ISO-2022-JP への変換ができていたのだと思います。 私の使用するバージョンの Cygwin の Perl でも正常です。 問題のある CentOS5.2 と 問題の無い Cygwin版で、関係しそうなバージョンの違いはありません。 CentOS 5.2: Perl 5.008008 Encode 2.12 Encode::JP 2.01 encoding 2.02 Cygwin: CYGWIN_NT-5.1 **** 1.5.25(0.156/4/2) 2008-04-17 12:11 i686 Cygwin Perl 5.008008 Encode 2.12 Encode::JP 2.01 encoding 2.02 できれば、すでに動いているCGIの use encoding('UTF8'); を直す事なく動く様にしたいのです。

  • utf8使用時のフォーム入力文字コード

    use utf8; binmode(STDIN,":encoding(shiftjis)"); をすると、入力時に内部変換がかかり、UTF-8として扱える という認識で使っていたのですが、下記のコードを実行してみると 入力した文字は普通に sjisで表示されます。 binmode(STDIN~ はどのような意味を持つのでしょうか。 http://www.lr.pi.titech.ac.jp/~abekawa/perl/perl_unicode.html http://www.hikoboshi.org/perl/doc/encode.html 他、いろいろなサイトで調べてみたのですが、いまひとつ 納得する回答を得ることができません。 //////////////////////////////////////////////////// use utf8; use open 'encoding(utf8)'; binmode(STDIN,":encoding(shiftjis)"); use CGI; print "content-type:text/html; charset=Shift_JIS \n\n"; print <<"EOM"; <form action="xxx.cgi"> <input type="text" name="hoe"> <input type="submit"> </form> ここは UTF-8文字列なので化ける<br> EOM $cgi = CGI->new; print $cgi->param('hoe'); //////////////////////////////////////////////////// 現在は下記のコードで自前で UTF-8に変換しています //////////////////////////////////////////////////// foreach( $cgi->param() ){ my $str = $cgi->param($_); $str = Encode::encode('utf-8', Encode::decode('shiftjis', $str)); $cgi->param( -name=>$_, -value=>$str ); }

    • ベストアンサー
    • Perl
  • ActivePerl を使用して MySQL に

    すいません、初心者です。 ActivePerl を使用して MySQL にアクセスしたいです。 windows7 64bit にActivePerlとMySQLを入れ込みました。 MySQLにアクセスするためのユーザー名やパスワードは root です。 login_data というテーブルを適当作成しました。 実際にアクセスしてみるには、どのようなコードを書けばいいでしょうか? =========================================== use strict; use warnings; # エラーをブラウザに表示 use CGI::Carp qw(fatalsToBrowser); use utf8; binmode STDIN, ':encoding(UTF-8)'; binmode STDOUT, ':encoding(UTF-8)'; binmode STDERR, ':encoding(UTF-8)'; use Encode; use DBI; # プログラム開始 # ユーザ名とパスワード my $user = 'root'; my $pass = 'root'; #データベースへ接続 my $db = DBI->connect('DBI:MySQL:localhost:login_data', $user, $pass,); if ( ! $db ){ print "エラー: $db->err $db->errstr\n"; } # 命令 my $sth = $db->prepare( "select * from test" ); # 実行 $sth->execute; # 出力 while( my @row = $sth->fetchrow_array ){ print "@row\n"; } #データベースから切断 $db->disconnect; __END__

    • ベストアンサー
    • Perl

専門家に質問してみよう