質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Perl

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

Q&A

解決済

7回答

5433閲覧

utf8プラグマを付けると 受け取った内容の一部が文字化けます。

motokix

総合スコア13

Perl

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

0グッド

0クリップ

投稿2015/12/16 01:41

編集2015/12/16 06:09

CGIモジュールを利用して、フォームを作成しているのですが、入力画面からフォームの内容を受け取る際
utf8プラグマを付けると受け取った内容の一部が 文字化けます。

現象はフォームから受け取ったコードが、Laten-1の文字コードが表示されてしまいます
以下のようにパラメーターを取得しています。

ソースコードは抜粋ですが、

HTML

1 2<input type="text" name="name" id="name" value="名前" size="30"> 3に対応するパラメーター 4<input type="hidden" name="fm_disp_name" id="fm_disp_name" value="お名前"> 5<input type="hidden" name="fm_need_name" id="fm_need_name" value="1">

fm_disp_項目名 があるものには、項目名を
fm_need_項目名
を受け取る仕組みです。

フォームから入力した全角文字が文字化けしてしまいます。
結果表示すると
お名前??????:Valueお名前値
のようになってしまいます。

perl

1コード 2#!/usr/bin/perl 3use strict; 4use Encode; 5use Encode::Guess; 6use Encode::EUCJPMS; 7#use Jcode; 8use CGI; 9use utf8; 10my $cgi = new CGI; 11require 'mimew.pl'; # jcode($subject)->mime_encode が動かないとき 12#エラーメッセージをブラウザに表示 13use CGI::Carp qw(fatalsToBrowser); 14use Data::Dumper; 15################################### 16print "Content-Type: text/html; charset=utf-8\n\n"; 17# 項目名 18my @item_names = (); 19# 項目の日本語表示名 20my %item_disp = (); 21# 項目の値 22my %item_value = (); 23# 項目の必須有無 24my %item_need = (); 25# フォームパラメータ 26my %fm_param = (); 27my $cgi_url = $cgi->url(); # このCGIのURL 28 29####################################################################### 30# 入力文字コード判定 31####################################################################### 32my $input_value = ''; 33my $name; 34my $value; 35my @values; 36foreach $name ($cgi->param) { 37 if ($name !~ /^submit$/i) { # 項目('submit'は除く) 38 @values = $cgi->param($name); 39 $value = join(',', @values); 40 if($cgi->param('fm_command') eq 'confsend') { # 確認フォームからのときデコード 41 $value = pack_param($value); 42 } 43 $input_value .= $value; 44 } 45} 46#エラーメッセージ配列 47my @err_namelist = (); 48 49#送信データがなければエラーとする 50if (0 == length($input_value)) { 51 push(@err_namelist, '呼び出しエラーです。管理者にお問い合わせ下さい。'); 52 print_input_error(@err_namelist); 53 exit; 54} 55 56#$input_jcode = Jcode::getcode($input_value); 57my $guess = guess_encoding($input_value, qw/euc-jp shiftjis 7bit-jis/); 58my $guess_encode = ($guess->name eq 'euc-jp') ? 'eucJP-ms' : $guess->name; 59 60####################################################################### 61# パラメータ読み込み 62####################################################################### 63 64my @checkboxes_array; 65 66foreach $name ($cgi->param) { 67 if ($name =~ /^fm_/) { # フォームパラメータ 68 $value = $cgi->param($name); 69 if($cgi->param('fm_command') eq 'confsend') { # 確認フォームからのときデコード 70 $value = pack_param($value); 71 } 72 #$value = jcode($value, $input_jcode)->sjis; 73 $value = ($guess->name ne 'shiftjis') ? encode('utf8', decode($guess_encode, $value)) : $value; 74 $fm_param{$name} = $value; 75 if($name =~ /^fm_disp_(\w+)/) { # 項目の日本語表示名 76 $item_disp{$1} = $value; 77 if(!defined $item_value{$1}) { 78 push(@item_names, $1); 79 $item_value{$1} = ''; 80 } 81 } elsif($name =~ /^fm_need_(\w+)/) { # 項目の入力必須 82 $item_need{$1} = $value; 83 } 84 } elsif($name !~ /^submit$/i) { # 項目('submit'は除く) 85 if(!defined $item_value{$name}) { 86 push(@item_names, $name); 87 } 88 @values = $cgi->param($name); 89 90 my $len = @values; 91 if ($len > 1) { 92 logger($name . 'はチェックボックスなのです!で項目数は' . $len . 'なのです!(ただし複数選択可能なselectタグかもしれない・・・けど項目の上限を決めておけばいい($lenが上限以下ならselectでも項目をCSVに出す等))'); 93 for (my $k=0; $k<$len; $k++) { 94 logger(@values[$k]); 95 } 96 #MEMO: 97 #確認画面で項目数がここでわかったので、後ろの画面に$nameは$len項目ですと渡せばcsvでいい感じにできるのです! 98 #実際の処理には現状確認画面でスペース?区切りか何かにされてしまっているため、項目数がうまくは取れないと思われる(確認画面のhiddenタグ参照の事) 99 my $object = { 100 name => $name, 101 len => $len, 102 #values => @values, 103 values => [], 104 }; 105 for (my $k=0; $k<$len; $k++) { 106 logger(@values[$k]); 107 push(@{$object->{values}}, @values[$k]); 108 } 109 push(@checkboxes_array, $object); 110 } 111 112 $value = join(',', @values); 113 if($cgi->param('fm_command') eq 'confsend') { # 確認フォームからのときデコード 114 $value = pack_param($value); 115 } 116 #$item_value{$name} = jcode($value, $input_jcode)->sjis; 117 $item_value{$name} = ($guess->name ne 'shiftjis') ? encode('utf8', decode($guess_encode, $value)) : $value; 118 119 if($item_disp{$name} eq '') { 120 $item_disp{$name} = $name; 121 } 122 } 123} 124 125my $len = @checkboxes_array; 126if ($len > 0) { 127 logger('新規オブジェクト配列は' . $len); 128 for (my $i=0; $i<$len; $i++) { 129 logger(@checkboxes_array[$i]->{name}); 130 logger(Dumper @checkboxes_array[$i]); 131 } 132} 133 134 foreach (keys %fm_param) { 135 print "\$fm_param\{$_\} = $fm_param{$_}<br>\n"; 136 } 137 foreach (@item_names) { 138 my $name = conv_html($item_disp{$_}); 139 my $value = conv_html($item_value{$_}); 140 print "$name:$value ($item_need{$_})<br>\n"; 141 142 } 143 exit; 144 145### csvに書けるように変換 146sub conv_csv { 147 my ($str) = @_; 148# $str =~ s/</&lt;/g; 149# $str =~ s/>/&gt;/g; 150 $str =~ s/\t//g; 151 $str =~ s/\r//g; 152 $str =~ s/\n//g; 153 $str =~ s/\,/"\,\"/g; 154 #$str =~ s/"/""/g; 155 return '"' . $str . '"'; 156 #return '' . $str . ''; 157} 158sub conv_tsv { 159 my ($str) = @_; 160# $str =~ s/</&lt;/g; 161# $str =~ s/>/&gt;/g; 162 $str =~ s/\t//g; 163 $str =~ s/\r//g; 164 $str =~ s/\n//g; 165 $str =~ s/\,/"\t\"/g; 166# $str =~ s/"/""/g; 167 return '"' . $str . '"'; 168 #return '' . $str . ''; 169} 170 171### htmlで表示できるように変換 172sub conv_html { 173 my ($str) = @_; 174 175 $str =~ s/</&lt;/g; 176 $str =~ s/>/&gt;/g; 177 $str =~ s/\t//g; 178 $str =~ s/\r//g; 179 $str =~ s/\,/&nbsp;/g; 180 $str =~ s/\n/<br>\n/g; 181 182 return $str; 183} 184 185### htmlで表示できるように変換 186sub conv_html2 { 187 my ($str) = @_; 188 189 $str =~ s/</&lt;/g; 190 $str =~ s/>/&gt;/g; 191 $str =~ s/\t//g; 192 $str =~ s/\r//g; 193 #$str =~ s/\,/&nbsp;/g; 194 $str =~ s/\n/<br>\n/g; 195 196 return $str; 197} 198sub conv_mail { 199 my ($str) = @_; 200# $str =~ s/</&lt;/g; 201# $str =~ s/>/&gt;/g; 202 $str =~ s/\t//g; 203 $str =~ s/\r//g; 204 $str =~ s/\n//g; 205 $str =~ s/\,/"\t\"/g; 206# $str =~ s/"/""/g; 207 return '"' . $str . '"'; 208 #return '' . $str . ''; 209} 210 211### hiddenパラメータに格納できるようにエンコード 212sub unpack_param { 213 my ($str) = @_; 214 215 $str =~ s/([^\w.\/\-@ ])/'%'.unpack('H2',$1)/eg; 216 217 return $str; 218} 219 220### エンコードしたhiddenパラメータをデコード 221sub pack_param { 222 my ($str) = @_; 223 224 $str =~ s/%([A-Fa-f0-9]{2})/pack('H2', $1)/eg; 225 226 return $str; 227} 228 229sub conv_ahtml { 230 my ($str) = @_; 231# $str =~ s/</&lt;/g; 232# $str =~ s/>/&gt;/g; 233 $str =~ s/\t//g; 234 $str =~ s/\r//g; 235 $str =~ s/\n/<br>\n/g; 236 237 return $str; 238} 239 240

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

argius

2015/12/16 04:28

一部が文字化けということですが、どのような文字の場合に文字化けするのでしょうか。全角文字はすべて文字化けしますか?
argius

2015/12/16 05:28

すみません、もう一点。送信フォームと確認用ページのHTMLは、charsetがutf-8になっていますか?
motokix

2015/12/16 05:34 編集

はい、なっております。 確認画面は、perlのスクリプトから別のファイルを読みますが、そこはUTF-8のファイルです。
argius

2015/12/16 05:48

承知しました。ありがとうございます。
guest

回答7

0

(追記)

最後の出力の直前に、utf8::decode($value)を行うことで、解消すると思います。

私もこの辺は詳しくないので、下記リンク先を参照してください。

一部引用:

use utf8 とは、 このCGIスクリプト内で扱う全角文字は文字コードがUTF-8であり、且つ、扱う全角文字データ全てにutf8フラグ(目印みたいなもの/以下単に「フラグ」と呼称)を付けてあるのでそのつもりで処理してね、とPerl側に教えてやる宣言のこと。

PerlのCGIのutf-8改造で文字化けしたときの処方箋
http://mycc.s33.xrea.com/data/pc/perl_use_utf8.html


(最初の回答)

残念ながら回答ではないのですが、(※回答を追記しました)
確かに再現はしましたので、ここにご報告します。

環境:

  • OS X 10.11.2
  • Apache 2.4.16
  • Perl 5.18.2

入力値には、環境依存文字でないとguess_encodingutf-8 or shiftjisと推測されてしまうので、環境依存文字を入力値に加えました。
(例:名前①)

解せないのは、ハッシュに値を設定した直後(116行目)では文字化けしていないのに、出力の箇所(139行目)では文字化けしてしまうという点です。conv_htmlを外しても文字化けします。

以上、他の回答者の方々の参考になれば。
私ももう少し調べてみます。

投稿2015/12/16 10:35

編集2015/12/16 13:16
argius

総合スコア9388

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

motokix

2015/12/16 12:05

私もここまでシンプルにして気づきました。をconv_htmlを外しても文字化けします。
argius

2015/12/16 14:36 編集

今度は解決方法について追記しました。お試しください。
motokix

2015/12/17 06:04

print "$name:$value ($item_need{$_}) <br>\n"; で囲まれた文字をprintしたときとそうでない時に違いが出ることがわかりました。
argius

2015/12/17 06:22

utf8::decode($value)は効果が無かったということでしょうか? 私の環境ではこれで解消したのですが。
motokix

2015/12/17 06:27

どの行に挿入しましたでしょうか?
argius

2015/12/17 06:52

> どの行に挿入しましたでしょうか? 138行目でハッシュから$valueを取り出した直後です。
motokix

2015/12/17 07:48

ダメでした。
argius

2015/12/17 08:00

あまり関係ないかもですが、mimew.plをコメントアウトしてました。 mimew.plはどこから入手したものでしょうか?
motokix

2015/12/17 08:18

どこからかはわかりません。
argius

2015/12/17 08:38

わかりました。mimew.plは適当なところからDLしました。 > print "$name:$value ($item_need{$_}) <br>\n"; > で囲まれた文字をprintしたときとそうでない時に違いが出ることがわかりました。 なるほど。 では、対症療法になりますが、 print "$name:", $value, " (%s)<br>\n"; のようにしてはどうでしょうか? これでダメなら、全く同じ環境で再現させて確認しないと解決は難しそうです。
motokix

2015/12/17 09:29

ダブルクオーテーションで囲まれた文字ですと変数展開されますが、その際、 UTF-8の解釈がおかしくなってしまうのでしょう。
argius

2015/12/17 09:53

print "$value"でも文字化けするでしょうか? utf8::decode()が原因だったら、仕様の問題といえると思うんですが、 ダブルクオーテーションで囲まれた文字の場合にだけ発現するというのはどうも腑に落ちませんね。 環境依存か古いバージョンのPerlが原因のバグの可能性もあるんじゃないでしょうか。 Perl 5.16.3でも試しましたが、こちらは同じ結果でした。
motokix

2015/12/17 10:03

print "$value"でも文字化けするでしょうか? いいえ、この地点では文字化けしておりません。 「グロブで取得した文字列は use encoding で指定した文字コードを見てそのコードから UTF-8形式に自動的に変換してから格納される とあるので近いです。
argius

2015/12/17 10:14

バグだとしたらお手上げですし、utf8プラグマによるトラブルはけっこうあるみたいです。 他に回答や案がなければ、ダブルクオーテーションで囲まない方法で対応したほうが早そうです。 お役にたてずに済みません。
motokix

2015/12/18 06:03

htmlにあるinput要素のname属性を日本語化した際にマッチングさせることは可能でしょうか?
argius

2015/12/18 06:09

name属性を日本語にすることは経験がないのでどうなるか分かりません。 今回の現象がどう影響するかも予想がつきませんね。
guest

0

ベストアンサー

コードをutf8で書いた上で、入力したらutf8からdecode()、出力前にutf8にencode()です。
プログラム内で文字列として扱えるのはdecode()の戻り値と、コード内に直接書いた文字列ぐらいです。

投稿2015/12/22 04:50

編集2015/12/22 05:53
pmint

総合スコア33

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

CGI.pm を使う場合は、pack(デコード)、unpack(エンコード)はなくても大丈夫のように思えます。
わたしの使っている環境(Ubuntu、Perl 5.14 )と違うのかもしれません。

投稿2015/12/16 07:58

yoichiro_ito

総合スコア103

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

HTML を Shift JIS で書いたケースはエラーになりました。
Perl は 同じコードです。
Encode::Guess->guess の 戻りが『No appropriate encodings found!』となります。

HTML ここから

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html lang="ja"> <head> <meta http-equiv="content-type" content="text/html; charset=Shift-JIS"> <title>テスト 20151216</title> <body bgcolor="#ffffff"> <b></b><br> <p> <form name="main" action="../../cgi-bin/tera_test_20151216/tera_test_20151216.cgi" method="post"> <table cellpadding="5" cellspacing="0" border="1"> <tr> <td> <input type="text" name="name" id="name" value="名前" size="30"> </td> </tr> </table> <input type="hidden" name="fm_disp_name" id="fm_disp_name" value="お名前"> <input type="hidden" name="fm_need_name" id="fm_need_name" value="1"> <input type="hidden" name="fm_command" id="fm_command" value="confsend"> <input type="submit" value="送信"> </form> </body> </html>

HTML ここまで

投稿2015/12/16 07:34

yoichiro_ito

総合スコア103

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

HTMLを UTF-8 で記述したケースでは、正常に動作しました。

HTML ここから

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html lang="ja"> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <title>テスト 20151216</title> <body bgcolor="#ffffff"> <b></b><br> <p> <form name="main" action="../../cgi-bin/tera_test_20151216/tera_test_20151216.cgi" method="post"> <table cellpadding="5" cellspacing="0" border="1"> <tr> <td> <input type="text" name="name" id="name" value="名前" size="30"> </td> </tr> </table> <input type="hidden" name="fm_disp_name" id="fm_disp_name" value="お名前"> <input type="hidden" name="fm_need_name" id="fm_need_name" value="1"> <input type="hidden" name="fm_command" id="fm_command" value="confsend"> <input type="submit" value="送信"> </form> </body> </html>

HTML ここまで

Perl ここから
#!/usr/bin/perl

use strict;
use utf8 ;

use Encode;
use Encode::Guess;
use Encode::EUCJPMS;
use CGI;
my $cgi = new CGI;
use CGI::Carp qw(fatalsToBrowser);
use Data::Dumper;
my @checkboxes_array;

print 'Content-Type: text/html;' ;
print "\n\n" ;
print '<html>' ;
print '<meta charset="UTF-8">' ;
print '<body>' ;

open my $OD,">>./log.txt";
foreach my $name ($cgi->param) {
if ($name =~ /^fm_/) {
my $value = $cgi->param($name);
print $name.'['.Encode::encode('utf8',Encode::decode(Encode::Guess->guess($value),$value)).']'.'<br>' ;
print $OD $name.'['.Encode::encode('utf8',Encode::decode(Encode::Guess->guess($value),$value)).']'.'<br>' ;
}
}
close $OD ;

print '</body>' ;
print '</html>' ;

Perl ここまで

投稿2015/12/16 07:31

yoichiro_ito

総合スコア103

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

わたしの環境(Perl v5.14.2 , Ubuntu 12.04.1 LTS)で、コンパイルエラーとなりました。
require はコメントアウトしました。
とりあえず気が付いたのは、42行目 for の ( に対する ) がない。71行目の行末に 逆引用符 ` がある点です。
実際に動かしてみると、挙動を追いかけやすくなるかと思いました。
公開可能な部分をペーストしていただけたのか、とは思います。

投稿2015/12/16 04:16

yoichiro_ito

総合スコア103

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

motokix

2015/12/16 06:16

抜粋コードで確認できるようになりました。 HTMLで以下のようなフォームを作成しCGIにsubmitしてください <form name="form1" method="post" action="test.cgi" id="form_id"> <input type="text" name="name" id="name" value="名前" size="30"> に対応するパラメーター <input type="hidden" name="fm_disp_name" id="fm_disp_name" value="お名前"> <input type="hidden" name="fm_need_name" id="fm_need_name" value="1"> </form> と fm_disp_項目名がfm_needは必須という風に分解されます。
guest

0

下記の 2点 を試してみてはいかがでしょうか?

(1) 2,3 行目あたりに下記の記述を挿入

use utf8 ;

(2) コードを保存するときの 文字コードを utf8 にする

投稿2015/12/16 02:34

yoichiro_ito

総合スコア103

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

motokix

2015/12/16 03:15

そのutf8プラグマを入れると、入力値が文字化けます。 ソースコードはUTF-8です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問