プロが教える店舗&オフィスのセキュリティ対策術

はじめまして

CGIを学んで1ヶ月の者です。
実は、仕事で少し面倒な依頼を受けて困っております。
内容はCSV形式のデータをPerlで読み取ってそれを
HTMLのテーブルに流し込むスクリプトなのですが、
慣れていないので思考停止をして進みません。

具体的なCSVデータの例ですが、

10,果物,りんご,ringo.html,ぶどう,budou.html,ばなな,banana.html,10,野菜,かぼちゃ,kabotya.html,なす,nasu.html,きゅうり,kyuri.html,魚,まぐろ,maguro.html,さけ,sake.html,10,肉,牛,usi.html,麺,ラーメン,ra-men.html,そば,soba.html,うどん,udon.html

まず、先頭に10があり、これは分類の区切りを示すヘッダーです。
次に果物でこれは、分類名です。
次にりんご、これは分類詳細名です。
つぎにringo.htmlでこれは分類詳細名のリンク先アドレスです。

このようなデータの並びですが、各分類と詳細項目は変動しますので
このテーブルのあるWEBページを毎回読み直す必要があります。

どうのようにしたらよいのでしょうか。
お助けください。

A 回答 (13件中1~10件)

CGIを学んで一ヶ月ということですが、


どのぐらいのことを理解されているのでしょうか?
split等の関数の存在とかもご存じない状態ですか?

基本的な流れですと(僕の思う基本ですが)
1. データを読み込む
2. 読み込んだデータを変数に代入する(ここでsplit等を使います)
3. 変数の個数情報なども利用しながら、foreachなどの繰り返し処理で表を作る
といったものになると思います。
どのへんでつまづいておられますか?

CGIの本や、WEBにもたくさんリファレンスがありますし、
1ヶ月もあればこのぐらいのプログラムならできると思いますよ。

補足していただければまた現れます。
他の方が答えてしまえばそのままお任せしますが。

この回答への補足

ご回答ありがとうございます。

わたくしのperlレベルですが、
かなり低いです。
今月の初めにperl入門書を買って、
チマチマとやっている程度です。
仕事はグラフィックデザインの担当ですが、
簡単なCGIくらい組めないとと思い学習しております。

現在、つまづいているのは、ご回答の手順の1番からです。
いろいろなperlのリファレンス本も買いあさりましたが、
基礎がないもので使いこなせない状態です。

なぜsplitを使わないといけないのか、またforeachを実際どのようにして今回のスクリプトへ取り入れるのかがピンときません。

その他に、読み込んだ配列を元に表をつくるには
if文とfor文で作りあげるのでしょうけど、
思考停止の状態でかんがえられないので困っています。

補足日時:2001/04/24 01:06
    • good
    • 0

1からなんですね。

了解です。

ちなみに他のプログラムは経験ありますか?
僕はもともとC言語はやったことあったので、
Perlを身につけるのは、簡単なリファレンスを見れば
最初から知っていたのと同じぐらいの感覚で使えましたけど。
しかし、まったくプログラムが初めてだと難しいかもしれませんね。
1がわからないとすると、もっと前の部分がわからない可能性がありますね。

簡単に先ほど箇条書きしたとこだけ説明しておきますね。
それ以前でもっとわからないならまた補足してください(笑)
ただ、僕も自分の書きやすいように書いていますので、
他の方が見たら、「もっときれいに書けるのに」って思われてしまうかもしれませんが、
そのあたりはどうかお許しください。
まずはこんな風にやっていけば良いんだなとわかっていただければいいかと思います。

1. データを読み込む
~~~~~~~~~~~~~~~~~~

if (!open(DB,"$file")) { &error(1); }
@lines = <DB>;
close(DB);

ってのが僕がよくやるファイルの読み込みです。
このまま使って、「@lines」とか「DV」という文字を適当に変更すればいいです。
errorの関数は、ファイルが開けなかった場合だけ呼び出されます。
(これも別のところで作成しなければなりませんけどね)
その必要がない場合は

open(DB,"$file");
@lines = <DB>;
close(DB);

でも良いかもしれません。
$fileには読み込みたいファイルにアクセスするパスを書きますが、
たいてい最初に

$file = "food.csv";

とか

$file = "../data/food.csv";

などと書いておけば大丈夫です。
もちろんファイルを開くところに直接書いても良いですね。

これがうまくいけば、「@lines」にcsvファイルが行単位で取り込まれます。
つまり

10,果物,りんご,ringo.html,ぶどう,budou.html,ばなな,banana.html
10,野菜,かぼちゃ,kabotya.html,なす,nasu.html,きゅうり,kyuri.html,魚,まぐろ,maguro.html,さけ,sake.html
10,肉,牛,usi.html,麺,ラーメン,ra-men.html,そば,soba.html,うどん,udon.html

というデータだとすると、

$lines[0] に果物の行がそのままは入り(「,」等も含んだ形で)
$lines[1] に野菜の行が入り
$lines[2] に肉の行が入ります

元のデータはすべて一行に表されているんですか?
そうだとしたら、全データが$lines[0]に入りますので、
後でデータを分割する必要がありますね。

2. 読み込んだデータを変数に代入する
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
次に書く配列に入れたデータを、より使いやすく小さく区切ります。
その際にsplitという関数を使います。
splitは、各行から特定のパターンで文字列を分割します。
今回のcsvファイルならば、「,」でデータが区切られているので、
「,」を切り分けのパターンとして「果物」「りんご」「ringo.html」などのデータを取り出します。

現在は、
$lines[0] = "10,果物,りんご,ringo.html,ぶどう,budou.html,ばなな,banana.html";
という状態でデータが入っていますので
最初の2つだけデータの意味が違うのでそこを切り出して、
さらに2つずつ切り出さないとだめですね(なんだか切り出しにくいかも)。
まず、
($no,$type,$data) = split(/,/,$lines[0]);
としてみたとします。
この場合、
$no = 10;
$type = "果物";
$data = "ringo.html,ぶどう,budou.html,ばなな,banana.html";
という風に変数に代入されます。
後は$dataにあるデータがまだ分割しきれていないのでさらに細かく分ければいいかと思います。
3つずつセットのようなので、
for文などをうまく利用すればすっきり分割できるかもしれません。

・・・あまりに中途半端ですが、
ちょっと寝る時間を大幅に過ぎてしまいましたので(朝6時起きなんですよね;;)
今日は寝ます(笑)

2の補足、3についてはまた書き込ませていただこうかと思いますが、
ご自分で本などをみてわかっていただければ幸いです。
自分でだんだん理解して、それがほんとに実現できるのがうれしいもんですしね。
他の方もどんどん回答してあげてください。

ではではまた~(^^;)/
    • good
    • 0
この回答へのお礼

夜遅くまで、考えていただいてありがとうございます。
まだ、解決しておりませんが、他の方もアドバイスして下さって本当に感謝です。

もうすこし、すっきりとしたらいいのですが、まだつかみきれてない感じです。

お礼日時:2001/04/24 18:52

解説するより実際の処理を見てもらったほうが早いかなと


思ってつらつらと書いてみました。

>このようなデータの並びですが、各分類と詳細項目は変動しますので
↑を「項目の並び順が変動」と勘違いして書いたので項目順序が変わっても
@TYPEを書き換えればいいようにしてみました。

$FILE = './hoehoe.csv';
$HTML = './hoehoe.htm';
@TYPE = qw( HEAD TYPE NAME ADDR );  # 項目のタイプを指定

open( IN,$FILE )||&error;
while( $line = <IN> ){        # 一行ずつ読み込む
 chomp( $line );           # 改行コードを削除
 &conv1( $line );          # 変換処理
}
close( IN );
open( OUT, '>'.$HTML );        # 出力ファイルを開く
 print OUT $output;         # 書き出す
close( OUT );
exit;                 # 終了


# 変換タイプ1
# ====================
sub conv1{
 my $line = shift;          # 渡された行の内容
 @lines = split( ',',$line );    # 「,」で分割して配列へ
 foreach $i ( 0 .. $#lines ){    # 0番目から配列の最後まで繰り返す
  if( $TYPE[$i] eq 'HEAD' ){    # 以降、番号に対応した項目で変換処理
  ~HEAD項目に対する変換処理~
  }elsif( $TYPE[$i] eq 'TYPE' ){
  ~TYPE項目に対する変換処理~
  }elsif( $TYPE[$i] eq 'NAME' ){
  ~NAME項目に対する変換処理~
  }elsif( $TYPE[$i] eq 'ADDR' ){
  ~ADDR項目に対する変換処理~
  }
  $outpt .= $lines[$i];       # 出力用変数に追加書き
 }
}


ところで
10,果物,りんご,ringo.html,ぶどう,budou.html,ばなな,banana.html,10,野菜,かぼちゃ,kabotya.html,なす,nasu.html,きゅうり,kyuri.html,魚,まぐろ,maguro.html,さけ,sake.html,10,肉,牛,usi.html,麺,ラーメン,ra-men.html,そば,soba.html,うどん,udon.html

ってひょっとして一行になってるんですか?

この回答への補足

いろいろありがとうございます。
今のところ、CSVデータは1行になっているものとして考えています。
それは、今後このCSVデータをデータベースで書き出してそれをperlで読み取って変動するWEBページを目指しているからです。
リンクするページが激しく変動するので手作業で直すことは避けたいための考えですが、
もし、1行じゃないほうがいいのでしたら、各分類ごとに改行して複数行にしても構わないと思います。

補足日時:2001/04/24 17:35
    • good
    • 0

質問にあったのが「決まった順番が横並びに連続する。

しかも数は変動」
という前提で作ってみたものです。「&conv1」→「&conv2」にする
以外は同様。

あとTABLEにするのなら「<tr></tr>」が必要というのを忘れてたので
それも追加しました。

# 変換タイプ2
# ====================
sub conv2{
 my $line = shift;          # 渡された行の内容
 @lines = split( ',',$line );    # 「,」で分割して配列へ
 foreach $i ( 0 .. $#lines ){    # 0番目から配列の最後まで繰り返す
  $x = (($i+1) % ($#TYPE +1)) - 1;
  if( $TYPE[$x] eq 'HEAD' ){    # 以降、番号に対応した項目で変換処理
   ~HEAD項目に対する変換処理~
   }elsif( $TYPE[$x] eq 'TYPE' ){
   ~TYPE項目に対する変換処理~
  }elsif( $TYPE[$x] eq 'NAME' ){
   ~NAME項目に対する変換処理~
  }elsif( $TYPE[$x] eq 'ADDR' ){
   ~ADDR項目に対する変換処理~
  }
  $output .= '<tr>' if $x == 0;   # 最初のやつなら<tr>追加
  $output .= $lines[$i];       # 出力用変数に追加書き
  $output .= '</tr>' if $x == -1;  # 最後なら</tr>追加
 }
}

# ====================
>$x = (($i+1) % ($#TYPE +1)) - 1;
↑ちょっとわかりにくいかもですね。^^;
何番目の項目かというのを判別するには、項目数で割った余り(%)を
求めればいいのですが、番号が「0」から始まるので「+1」してます。

「$#TYPE」は「@TYPE」の最後の添え字、この場合「3」が入ってます。
項目数は「4」ですがやはり「0」から始まるので「+1」してます。

1番目 … ((0+1)%(3+1))-1 = 0
2番目 … ((1+1)%(3+1))-1 = 1
3番目 … ((2+1)%(3+1))-1 = 2
4番目 … ((3+1)%(3+1))-1 = -1
5番目 … ((4+1)%(3+1))-1 = 0
6番目 … ((5+1)%(3+1))-1 = 1
7番目 … ((6+1)%(3+1))-1 = 2
8番目 … ((7+1)%(3+1))-1 = -1
9番目 … ((8+1)%(3+1))-1 = 0

4番目は「3」を出したいところですが、Perlは配列の何番目かを
指定するとき「-1」すると、後ろから、つまり最後のものを取得
してくれるのでこのままにしてます。他の言語ではダメかも。^^;

~変換処理~のところは
$line[$i] =~ s/^(.+)$/<td>$1<\/td>/;
もしくは↓のほうがスッキリ
$line[$i] =~ s|^(.+)$|<td>$1</td>|;

ぬ、とてもPerl歴一ヶ月の人に教えるようなものではないですね…。^^;
まぁこういう方法もあるよ程度に留めておくといいかも。

この回答への補足

本当にお世話になっております。
最終的には下記のレイアウトで表示させたいのですが、
http://www.niigata-megumi.co.jp/rdn/
トップページから「取扱品目」のリンクをクリックしてください。
そうすると表が出てくると思います。
そのようなレイアウトの表にCSVデータを流し込んで、
リンクさせていきたいと考えております。
この表内に流すCSVデータですが、
ある分類がポッカリなくなったり、ある分類内の詳細名が増減することを想定しないといけないもので、やっかいに感じてます。
CSVのレコードフォーマットはやりやすいように考えてもいいとクライアントから言われています。

補足日時:2001/04/24 18:47
    • good
    • 0
この回答へのお礼

ありがとうございました。
後は、なんとか自力で考えていきます。

perlはUNIXの知識も必要になるので、
習得しずらいのかな感じてます。

お礼日時:2001/04/24 20:50

一行にすると &conv2 のような余計な行程が必要になってくるから


やめたほうがいいです。ExcelやAccessの画面をイメージしていただけると
わかると思いますが、

HEAD TYPE NAME ADDR
HEAD TYPE NAME ADDR
HEAD TYPE NAME ADDR

という感じに並ぶのが一番効率がいいです。
TYPEが存在しない場合は空欄に、ひとつの項目に複数存在する
場合は「/」とか、“絶対使われない文字”で区切るのがいいです。

head(TAB)type(TAB)name,name2(TAB)addr
みたいな感じにTABで区切り、項目内をさらに分割するときは
「,」みたいな。

# 現実逃避終了したので以降の回答がニブくなると思われます(笑)

この回答への補足

すみません教えていただきたいのですが、
@TYPE = qw( HEAD TYPE NAME ADDR );
という記述がありますが、例えばデータが 10,果物,りんご,ringo.htmlとなっていた場合に、
HEAD TYPE NAME ADDRにはどう記述すればよいのでしょうか。HEADは10 のような気がしますが、他の場合変わってしまうのでどうしたらいいのかわかりません。

補足日時:2001/04/26 11:53
    • good
    • 0

どうもこんばんは。


再び登場です。

>ある分類がポッカリなくなったり、ある分類内の詳細名が増減することを想定しないといけないもので、やっかいに感じてます。

hoomaさんは変動するデータや、項目の変動が激しいことを気にしていらっしゃるようですが、
自作のCGIをいくつか遊びで作ってきた経験から言わせてもらいますと、
そういう変動するデータのためにこそCGIが有効ではないかと思います。
htmlでは固定的な表現しかできませんが、プログラムを使うことの利点は
アクセスする度に変動的な表現ができることではないでしょうか?

項目が変動した場合にもばりばり動かすCGIを作ろうと思っているなんて
個人的には楽しそうでうらやましいです(笑)
僕も仕事でそういうの任されてみたいです(^^)

どのように変動データに対応するかは
CGIプログラムの問題ではなくて
アルゴリズムの問題だと思います。

まあPerlのプログラミングとして、
どのようにデータを加工するのがやりやすいか
などを気にする必要もあるので、
アルゴリズムとプログラムは同時に考えた方がいいですけどね。
ある程度経験のある人なら、
元データがこういう形の方が扱いやすいと言うこともわかると思います。

お礼を見ていると、「これ以上答えてもらうのは悪い」という感じが伝わってきましたが、
そんなことありませんので、もっと具体的な困っている箇所をあげてくだされば、
一つずつ進んでいけるのではと思いますよ。

あと、特にUNIXの知識なんて必要ないような気がしますが。

この回答への補足

あたたかいお言葉ありがとうございます。
他の補足でも説明しましたが、
http://www.niigata-megumi.co.jp/rdn/
「取扱品目」のページのようなレイアウトで
表はCSVで流し込みをするということです。

とあるシステム系の会社のサイト構築を頼まれて、デザインを主にやってます。
そのなかで、CGIがたくさん絡んできており、ほとんどが外注に出して出来上がってますが、クライアントは一方ついてから、今回のようなスクリプトを用意してくれと言ってきたのでGW前に納品しないといけないので切羽詰っています。

今、私が自分で確認してできているスクリプトは
表(中身は空)をHTMLに書き出すCGIだけです。

CSVを読み込んで、配列にいれて細切れにして、データの種別」を判断し、テーブルに書きだすというものが
使い慣れていないのでアドバイスをいただいても「あーそうか!」と言って実行に移せないのです。
こわがっているのかもしれません。

でも、明日は1日perlと闘えそうなので、じっくり取り組んでみます。

補足日時:2001/04/24 22:38
    • good
    • 0
この回答へのお礼

お礼ではのですが、補足をしたくても
入れる場所がないのでこちらで失礼します。

いろいろ試して、ようやく読み込んだCSVデータを配列に入れてsplitをかけて、細切れにすることはできました。(ただし、1行だけ)
そこで、教えていただきたいのですが、全行を多次元配列などにしてsplitをかけるにはどうしたらいいでしょうか。
foreachなどで試しましたが、うまくいきません。

お礼日時:2001/04/26 12:05

多次元配列って使ったことないんですが、



@datasに各行のデータが入っているとして、

print "<table>\n";
foreach $data (@datas){
 $dataを細切れにする
 細切れデータから表を作る
}
print "</table>\n";

のような感じではできないでしょうか?

この回答への補足

現在、わたくしが出来ているところまで掲載いたします。
#!C:\perl\bin\perl
#ファイルの読み込み
$file = "sample.csv";
open(DB,"$file");
@lines = <DB>;
close(DB);

#改行データ除去
chomp($lines);

#リストの細分化
foreach $output (0..$#lines){
@table= split(',',$lines[0]);

}

foreach $i(0..$#table){
print $table[$i],"\n";
$i+=1;
}

それで、問題なのですが、上記のリスト細分化の部分は今のところ1行しか処理できないようになっていますが、これを全行処理させたいのです。
それとも、この部分をサブルーチンにして、
FOR文で繰り返して、行を変えながらサブルーチンに飛ばしたほうがよいのでしょうか。
その場合、最終行を見分ける方法がわかりません。


どうしたらよいでしょうか。

補足日時:2001/04/26 16:40
    • good
    • 0

No.4の返答への返答です。


この返答で私が書いた「HEAD TYPE NAME ADDR」というのは

10,果物,りんご,ringo.html,ぶどう,budou.html,ばなな,banana.html,10,野菜,かぼちゃ,kabotya.html,なす,nasu.html,きゅうり,kyuri.html,魚,まぐろ,maguro.html,さけ,sake.html,10,肉,牛,usi.html,麺,ラーメン,ra-men.html,そば,soba.html,うどん,udon.html

というデータよりも

10,果物,りんご,ringo.html
10,果物,ぶどう,budou.html
~略~
10,麺,そば,soba.html
10,麺,うどん,udon.html

「1データ1行で、1行は同じ項目数」という感じに元データを作り直して
もらったほうがいいということです。

で、今気付いたんですが、
A,B,C,D,A,B,C,D,A,B,C,D,
って感じに項目数は同じなのかと思いました。
A,B,C,D,C,D,C,D,
なんですね。これだとNo2、No.3のサンプルはまったくのゴミです。^^;

提示された形式だと、「中に入っているデータがどんな形式なのか」を
調べないといけないし、もしデータに規則性がないなら判断のしようも
ありません。それとも「10」という数字がデリミタ(分割指定子)ですか?

いずれにせよ、もし私がこの仕事をやるハメになったとしたら
「こんなデータではやってられません。元データを作り直してください」
ってとこからスタートします。^^;

この回答への補足

たびたびありがとうございます。
やはりデータを見直したほうがいいですね。
それで、データの種別をはっきりわかるようにするために
10,分類,20,詳細名,30,リンク先,20,詳細20,詳細名,30,リンク先,名,30,リンク先
10,分類,20,詳細名,30,リンク先
10,分類,20,詳細名,30,リンク先,20,詳細20,詳細名,30,リンク先,名,30,リンク先
のようにしたほうが作りやすいのかなと思いこういう形式で現在考えてます。
これでしたら、楽になりますでしょうか。

補足日時:2001/04/26 15:23
    • good
    • 0

>10,分類,20,詳細名,30,リンク先,20,詳細20,詳細名,30,リンク先,名,30,リンク先



それだったら
10_A,20_B,30_C,20_B,30_C~
って感じに「データの中に判別子を埋める」ほうがいいです。
「今“10”だったから次のデータは“分類”だ」よりも
「頭が“10”だからこのデータは“分類”だ」のほうがソースが
見やすくなります。頭の「10_」とかは後で消せばいいわけです。

しかし本当は「位置でデータ種別を判別させる」ほうが簡潔だし
処理も早くなります。なぜに下記のような表計算リスト的なデータ
にしないのでしょうか?
A,B,C,D
A,B,C,D

A,B,C,D
,,C,D
↑でもいいですから、とにかく「項目の位置は固定」させたほうが
いいです。実際にくんでみればわかります。

この回答への補足

ありがとうございます。
danyさんのおっしゃるとおり、データの中に識別子を付けた場合、後で、取り除くための記述はどうしたらいいでしょうか。

補足日時:2001/04/26 16:49
    • good
    • 0

>データの中に識別子を付けた場合、後で、取り除くための記述はどうしたらいいでしょうか。



$hoe ~= s/^10_//;
です。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!