• ベストアンサー

テキストファイルを読み込み出力で文字化け

まだパールを始めたばかりの初心者ですがよろしくお願いします。 現在cgiをPerlで記述します。 「|」区切りのテキストファイルを行ごとに読み込みsplitを利用して項目別に区切っています。 そしてそれをhtmlで項目別にテーブルに表示するというのをやっているのですがテキストファイル中に「鋼」という文字があると「・br>」という表示になり勝手にそこで区切られて以降違う項目になってしまいます。 それ以外はうまく表示出来ますし前後にスペースやメタ文字があったりとかではなく「鋼」を消すとうまく表示出来ます。 処理はこんな感じです。 ~テーブル内部~ open(IN, a.txt); @getline = <IN>; foreach $linedata (@getline) {  chop $linedata;  (@importdata)=split /\|/, $linedata;  print "<tr>";  foreach(@importdata){  $_=~ s/\n//g;  $_=~ s/\r//g;  print "<td>".$_."<br></td>"; } print "</tr>"; 解決法方が分かる方、教えていただけないでしょうか? よろしくお願いします。

  • Perl
  • 回答数5
  • ありがとう数4

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

  • ベストアンサー
  • taseki
  • ベストアンサー率66% (155/233)
回答No.4

やりかたはいろいろあると思いますが、とりあえず解りやすいものとして、 以下サンプルです。 ------------------------------- # SJIS文字 $character_sjis = "(?:[\x00-\x7F\xA1-\xDF]|(?:[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC]))"; foreach (@getline) { tr/\x0D\x0A//d; $_ .= '|'; print "<tr>\n", (map "<td>$_</td>\n", /\G($character_sjis*?)\|/g), "</tr>\n"; } ------------------------------- 前後に print "<table>\n"; と print "</table>\n"; があり、@getlineに全行が読み込まれている、という前提です。 また、質問文の改行処理をちょっと直してあります(後で全ての改行を削除しているのでchopは不要、また改行文字の削除は「tr/\x0D\x0A//d」が良い)。 簡単に説明しますと、まず「SJISの1文字」を正しく認識させる正規表現を$character_sjisにセットしておきます。 各項目の分割方法は、 【条件】 「SJISの1文字」が“0文字以上”続いた後に「|」があるもの という部分を探します。この条件にあったものを1つの項目と見なします。 しかしこれでは、一番右の項目だけは、「後に「|」があるもの」という条件を満たせませんから、まず探す前に末尾に「|」を付けています。 そして探すとき、とても重要なことが2つあります。 1.必ず先頭から探し始めなければいけません。なぜなら、たとえば「鋼」という文字の後半部分は 7C ですから、この部分だけで「SJISの1文字」という条件を満たしてしまいます(同じ問題を含む文字が他にもあります)。 ちゃんと前半部分も見る、そのためには先頭から順番に探していかなくてはなりません。それをやるのが「\G」です。 2.上記の【条件】では、1行全体が条件を満たしてしまいます。つまり、 「項目A|項目B|項目C|」という文字列“全体”が、条件を満たしています。間にいくつか「|」が入っているのに、です。 そこで、最短マッチを使います。条件を満たす「最短」のものを探す、という指定です。それをやるのが「*?」です($character_sjisを、“「|」以外のSJIS”という定義に変える方法もありますが、汎用性を考えるとこのほうがいいと思います)。 サンプルでは、分割してtdタグで囲んで出力、というのを1行に書いてしまっていますが、たとえば画像も表示するなどの場合は、わけて書いた方が読みやすいかもしれません。 ------------------------------- @cells = /\G($character_sjis*?)\|/g; print "<tr>\n"; foreach (@cells) { print "<td>$_</td>\n"; } print "</tr>\n"; ------------------------------- ******************************************* moon_piyoさんの方法について、大変恐縮なのですが、ちょっと気になる点があります。私が間違っていたら申し訳ありません。 1.書かれた正規表現は、  ---------------------   A: SJISの2バイト文字である  または、   B: 「|」以外の文字である  --------------------- が連続している部分を探していると解釈したのですが、これではBがすでにAも含んでいて、Aが意味をなさないように思えるのですが…。つまり、 /((?:[^\|])+|)\|/g; と書いたのと同じなのでは…。 試してみたところ、一番右に「鋼」があると正しくマッチしませんでした。 2.上記Aの「SJISの2バイト文字である」という部分、「|」の前が1バイト文字だった場合はマッチしないのでは。 3.「鋼」という文字は「[^\|]」があるためマッチしなくなってしまうのでは。 4.一番右の項目がマッチしないのでは。

その他の回答 (4)

  • taseki
  • ベストアンサー率66% (155/233)
回答No.5

> 最後の「\|」を取れば~ > こんな感じになります。 > 項目1||項目3|項目4||項目6 > が > ('項目1','','','項目3','項目4','','','項目6') > と格納されます。 試していませんが、実際には以下のように格納されていませんか? “項目1”, “”, “”, “項目3”, “”, “項目4”, “”, “”, “項目6”, “” すべての項目の部分と、「何もないところ」もマッチするためだと思います。

gomara-yu
質問者

お礼

回答ありがとうございます。 このテキストファイル実は厄介でして何故行単位で処理していたのかというと1つのデータの項目が6行ありまして、1行目は「,」区切りで他5行は「|」区切りというものなのです。(しかも一行目の内容によっては下5行が無いパターンも存在するというものでして手を焼いておりました) tasekiさんのサンプルを参考にして何とか希望通りの表示が出来ました。本当にありがとうございます。 配列の中身はおっしゃる通りでした。

  • moon_piyo
  • ベストアンサー率60% (88/146)
回答No.3

ANo.1のものです 空文字の正規表現= ←なにもない(長さ0)です それから 項目1||項目3|項目4||項目6| と項目のすぐあとに|がついているようですので (Shift-JIS定義文字 又は |以外の文字 又は 空文字)が1回以上続き それに|が続く、という正規表現ではどうでしょうか(■の部分を に置き換えて 最後に\|をつけました) (@importdata) = $linedata =~ /((?:[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC]|[^\|]|)+)\|/g; 同様に (@importdata) = $linedata =~ /((?:[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC]|[^\|])+|)\|/g; あるいは (@importdata) = $linedata =~ /((?:[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC]|[^\|])*)\|/g; とか...

gomara-yu
質問者

お礼

回答ありがとうございます。 「何も無い」というのに何時間も悩まされており助かりました。 本当に申し訳ないのですが実は…今もう一度確認した所一番最後の項目だけ|が最後についていなかったのです。(度々すみません) 当たり前ですがこの場合だとヒットしないので最後の項目が配列に入りません。 最後の「\|」を取ればShift-JIS定義文字 又は |以外の文字 又は 空文字が1回以上続くになると思って実行すると空文字の所が2度ヒットしてる(?)のか一個の空文字で2つの空の要素が格納されてしまいます。 こんな感じになります。 項目1||項目3|項目4||項目6 が ('項目1','','','項目3','項目4','','','項目6') と格納されます。 私の考え方が甘すぎるのでしょうか。 親切の教えていただいてるのに何度もすみません。 よろしくお願いします。

gomara-yu
質問者

補足

何度もサンプルをありがとうございました。 本当に勉強になったと思います。

  • taseki
  • ベストアンサー率66% (155/233)
回答No.2

まずは、なぜ元のスクリプトではおかしなところで区切られて、ANo.1の方の方法でうまく区切られたのかを理解すれば、自ずと答えは見えてくると思います。 「鋼」という文字は、Shift-JISで 8D7C です。そして「 | 」という文字は 7C です。 8D7C の後半部分を区切り文字と認識してしまった、ということです。 そしてANo.1の方の方法は、Shift-JIS定義文字に続く「 | 」、という判断にしたので、正しく区切られました。 この「Shift-JIS定義文字に続く~」という部分を、「Shift-JIS定義文字があるか、あるいは何もない、に続く~」というようにすればいいことになります。 perlで日本語を扱う場合は注意が必要です。以下が役に立つと思います(CSV区切りの方法も載っています)。 http://www.din.or.jp/~ohzaki/perl.htm

gomara-yu
質問者

お礼

回答ありがとうございます。 正規表現がまだ知識が皆無の状態でしたのでtasekiさんに回答いただいた後、参考ページ等色々勉強しにいったのですがうまく出来ません。 /((?:[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC]|[^\|])+)/g; 私の勉強不足かもしれませんがShift-JIS定義文字又は|以外の文字が一回以上続くものというのは分かりましたが(間違っていたらすみません)空文字というのはどう表したらいいのでしょうか。 またイメージとしては /((?:[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC]|[^\|]|■)+)/g; の■に空文字を表すものを書けばいいのでしょうか? それとも /((?:[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC]|[^\|])+|■)/g; でしょうか? もの分かり悪くてすみません。

  • moon_piyo
  • ベストアンサー率60% (88/146)
回答No.1

こんちは まずは下記のように変更してみてどうでしょう.. (@importdata)=split /\|/, $linedata; ↓ (@importdata) = $linedata =~ /((?:[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC]|[^\|])+)/g;

gomara-yu
質問者

お礼

回答ありがとうございます。 教えていただいた方法で表示出来ました。 本当にありがとうございます。 Shift-Jisが問題だったのでしょうか? 正規表現はまだ詳しく分からないのでこれから勉強していきたいと思います。

gomara-yu
質問者

補足

お礼を言った後申し訳ないのですがもう一度確認すると項目が無い場合配列には空のデータを無視して次のデータが入っている状態でした。 例えばテキストファイル内で 項目1||項目3|項目4||項目6| となっていると配列には ('項目1','','項目3','項目4','','項目6') と空も入って欲しいのです。 教えていただいたのでは ('項目1','項目3','項目4','項目6') となってしまいます。 もう一度お願いできないでしょうか? よろしくお願いします

関連するQ&A

  • 文字化けを直す方法

    ホームページ作成中です。 あるCGIで出来たリストの一部だけをSSIで表示させようとしています。 イメージとしては買い物籠CGIの登録されている商品名だけを一覧表示みたいなものです。 リストのdatファイルは「EUC」で保存されています。 下の表示させるCGIと表示先のhtmlは「S-JIS」で保存しています。 リスト表示は他のCGIからコピーして少々変更しただけの物です。 ------------------------ #!/usr/bin/perl require './jcode.pl'; #jcode::convert(\$str, 'euc', $code); print "<center>\n"; print "<TABLE>\n"; print "<TR><TD>語録一覧</TD>\n"; print "</TR>\n"; print "<TR><TD>\n"; open (FILE2,"./data/word.dat"); @file = <FILE2>; close FILE2; $no = -1; foreach (@file) { $no++; # local($file,$number,$word,$yomi)= split /\t/,$data[$i]; local($file,$number,$word,$yomi)= split /\t/; print "$word<BR>\n"; } print "</td></TR>\n"; print "</table>\n"; print "</CENTER>\n"; exit; ------------------------------- こうすると、リスト($word)が文字化けします。(英数は化けずに表示。って当たり前ですね) どうにかしてEUCのリストのみ表示するときS-JISに変換する方法などないでしょうか? よろしくお願いします。

    • ベストアンサー
    • CGI
  • phpで名簿データを出力

    名簿データを開き、一枠に番号・名前・住所・電話・メールを書き出し一列に4名分のデータを並べます。 CGIだと以下の様なイメージかと思いますが、今回はPHPで書くように指示されました。 勉強不足で月曜迄に出来る気がしません。どのように書けば良いかご教示よろしくお願いします。 open (IN,"meibo.txt"); @abc = <IN>; close (IN); print = "<table border=1><tr>\n"; foreach (@abc) {($id,$name,$address,$tel,$mail) = split(/<>/); print <<HTML; <td>$id<br>$name<br>$address<br>$tel<br>$mail</td> HTML $count++; if ($count > 4){print = "</tr><tr>\n"; $count = 0;} } print = "</tr></table>\n";

    • ベストアンサー
    • PHP
  • ログファイル表示で全部表示されたりされなかったり…

    ログファイルをオープン、その中のユーザー名・パスワードと入力されたユーザー名・パスワードを比較して、どちらも一致した場合のみ表示するようなものを作成しました。 ただ単に表示を行うと一致するものすべて表示されるのですが、テーブルなどを使用して表示させると数が減って表示されるのです。 これはいったいなぜでしょうか? ---------- for ($i=0;$i<$gyou;$i++){ @atai = split(/&/,$log[$i]); foreach $atais(@atai){ ($keys, $values) = split(/=/, $atais); $FORMS{$keys}=$values; }#foreach if($FORM{'USER'} eq $FORMS{'USER'}){ if($FORM{'PASSWORD'} eq $FORMS{'PASSWORD'}){ print "$FORMS{'TITLE'}<BR>\n"; print "<BR>\n"; だと全件(19件入力中、一致は18件。18件すべて)表示 一番下を print "<TABLE>\n"; print "<TR><TD>$FORMS{'TITLE'}</TD></TR>\n"; print "</TABLE><BR>\n"; print "<BR>\n"; と変更すると13件しか表示されません。

    • ベストアンサー
    • CGI
  • テキストファイルのHTMLタグを文字として表示させるには?

    テキストファイルのHTMLタグを文字として表示させるにはどうすればよいのでしょうか? <HTML>の"<"をJIS文字コードに置き換えれば良いというようなことを聞きましたが、 $_ =~ tr/</&lt);/s; この文字を置き換える式では駄目でした。 多分表記の仕方が間違っていると思うので正しいのをよろしくお願いします。 (上の置き換え文だと&HTML>になってしまいます。) #!/usr/bin/perl main:{ require "jcode.pl"; $file = 'file01.txt'; print "Content-type: text/html\n\n"; print "<HTML><BODY>\n"; print "<TITLE>表\示</TITLE>\n"; open( FH, $file) or die "Can't open"; foreach( <FH> ){ $_ =~ tr/</&lt);/s; print $_ ."<br>"; } close(FH); print "<br>_<br><"; print "</BODY></HTML>\n"; }

    • ベストアンサー
    • Perl
  • 複数のCSVファイルを1つの表にして出したい

    あるCSVファイルを、Perl+HTMLで、プラウザに「表」で表示させるようにしました。 しかし、追加で複数のCSVファイルを読み込ませて、同じ表に入れることができません。(今ある表のなかの、$r1,2,3と番号が存在してる以外の空欄になっている表に入れたいのです) どのようにすればいいのか、どなたかお教え下さい!>< ちなみに、今作ってあるものはこんなかんじです。 動作確認済み。 #!/usr/local/bin/perl #----------------------------------- $file = "a.20090504.csv"; #----------------------------------- print "Content-type: text/html\n\n"; print "<html>\n"; print "<body>\n"; print "<br><br>\n"; print "<center>\n"; print "<table border=1 >\n"; $days = $file; $days =~ s/a.//g; $days =~ s/.csv//g; open(IN,"$file"); @kasou = <IN>; close(IN); foreach $line (@kasou){ ($r1, $r2, $r3, $r4, $r5, $r6, $r7) = split (/,/, $line); print "<tr>\n"; print "<td>$days</td>\n"; print "<td>$r1</td>\n"; print "<td></td>\n"; print "<td>$r2</td>\n"; print "<td></td>\n"; print "<td></td>\n"; print "<td>$r3</td>\n"; print "<td></td>\n"; print "<td></td>\n"; print "<td>$r4</td>\n"; print "<td>$r5</td>\n"; print "<td>$r6</td>\n"; print "<td>$r7</td>\n"; print "</tr>\n"; } print "</table>\n"; print "</body>\n"; print "</html>\n"; ※ちなみに、これですとCSVファイル名の20050504だけがtrに入るようになっています。これは、こうしたいのでこうしているのですが、繰り返しtrに投入されるようになっているのも気になります。 どうしたら直るでしょうか。

  • テキストデータから指定した1行を抜き出して表示

    perl初心者です。 説明不足の箇所もあるかと思いますが、よろしくお願いします。 テキストデータから指定した1行を抜き出して表示するのにはどうしたらいいんでしょうか。 data.txtの中に=で区切られた文字が入っているとします。 1=にんじん=150 2=じゃがいも=200 3=たまねぎ=100 左から$s_no,$s_name,$s_priceという変数になっています。 これを print <<"EOF"; <TABLE class="list_index"> EOF open(IN,"../indata/sdeta.txt"); @data = <IN>; close(IN); foreach $sdata (@data){ chop($sdata); ($s_no,$s_name,$s_price) = split (/=/, $sdata); print <<"EOF"; <TR> <TD class="list2">$s_no</TD> <TD class="list3">$s_name</TD> <TD class="list4">$s_price円</TD> <TR> EOF } print <<"EOF"; </TABLE> EOF という感じでテーブルにデータを全て一覧表示させました。 ここから、$s_noで表示されている「1」「2」「3」をクリックすると、 別ページにとんで、クリックした番号の1行を編集用のフォームに表示させたいです。 フォームに表示させる方法はわかっているんですが、 クリックで指定した1行だけを表示する方法がわからずに困っています。 よろしくお願い致します。

    • ベストアンサー
    • Perl
  • perl 計算結果をファイルへ出力したい

    perl やり始めたばかりです。宜しくお願いします。 入力ファイル data.txt があるとします。 data.txt は、 123 456 789 333 555 777 以上のようなテキストファイルといたします。このファイルを 以下の様に100分の1にして出力したい。 1.23 4.56 7.89 3.33 5.55 7.77 と言うことで、この場で教えていただきました。それが、以下です。 #!/usr/bin/perl open(IN, "data.txt") or die ; @x = <IN>; close (IN); foreach $line (@x){ chomp($line); @elms = split(' ',$line); foreach $data (@elms){ print $data/100," "; } print "\n"; } おかげ様でこれはこれで上手く動きました。そこで、出力値をファイルに 書き込みたいのです。 もちろん、以下の様な方法でファイルに 書き込めるのは判っております。 計算プログラム.pl > outfile.txt しかし、上のプログラムをベースにファイルに書き込めないかと色々と 試してはみましたが、どうも上手く行きません。 どなたか教えて頂けないでしょうか? 宜しくお願い申し上げます。

    • ベストアンサー
    • Perl
  • データ

    データが送れているのかをプリントで確かめたのですが、$xの値しか表示されません。 セレクトボタンで選択した値が代入できません。 データが送られていないのでしょうか? どこが間違っているのか教えてください。 #!c:/perl/bin/perl <<省略>> print "<table border=1>"; print "<tr><td>商品番号</td><td>商品名</td><td>価格</td>"; print "<td>残り個数</td><td>注文数</td></tr>"; open(RF,"<butu.dat"); while(<RF>){ @data=split; print "<tr><td>$data[0]</td><td>$data[1]</td><td>$data[2]円</td>"; print "<td>$data[3]個</td><td><select name='ko[$n]'>"; for($i=1;$i<=$data[3];$i++){ print "<option value=$i>$i</option>"; } print "</select></td></tr>"; $n++; } close(RF); print "</table><br><br><input type='hidden' value='$n' name='gyo'>"; <<省略>> exit; ↓↓↓ #!c:/perl/bin/perl print "Content-type: text/html\n\n"; require 'cgi-lib.pl'; &ReadParse(\%in); $x=$in{'gyo'}; for($n=0;$n<$x;$n++){ $d[$n]=$in{'ko[$n]'}; } <<省略>> exit;

    • ベストアンサー
    • Perl
  • 画像とテキストのレイアウト

    左に画像、その画像の右に複数行のテキストを、画像高さの中央に配置したいと思っています。 <table><tr><td><img></td> <td style="vertical-align:middle"> 文1<br> 文2<br> 文3<br> </td></tr></table> をテーブルを用いないでレイアウトしたいということです。 <img src="loc"> 文1<br> 文2<br> 文3<br> では、文1 のみが画像右に表示されます。 <img src="loc" style="float:left"> 文1<br> 文2<br> 文3<br> では、文全体が画像のtop位置から表示されます。 どのようにスタイルシートで指定すればいいのでしょうか?

    • ベストアンサー
    • HTML
  • テキストの回り込みができない?

    テーブルで縦2列に区切って、左側には固定幅の表を作っています。 右側には残りの幅を使った表(更新履歴)をつくりたいと思って、<img>タグにalign属性を使ってテキストが回りこむようにしたつもりなのですが、テキストが回りこまず、表示される位置まで列の幅が広がってしまうのですが、どこか間違っているのでしょうか? <table border="0" width="100%"><tr>  <td valign="top" width="450">   <table><tr>     <td>...</td>     <td>...</td>   </tr></table>  </td>  <td valign="top">   <table border="0" width="100%"><tr>    <td class="cel1" align="left">     <b>タイトル</b>...日付    </td>   </tr><tr>    <td class="cel2" align="left">     コメントコメントコメントコメント    </td>   </tr><tr>    <td class="cel1" align="left">     <b>タイトル</b>...日付    </td>   </tr><tr>    <td class="cel2"><img src="./img.gif" align="left">     コメントコメント<br>コメントコメント    </td>   </tr></table>  </td> </tr></table>

    • ベストアンサー
    • CSS

専門家に質問してみよう