トップへ
田村研究室

インターネット利用技術演習

2009年度

2009
7/13

CGIによる自動集計

アンケートの集計処理

アンケートデータの受取

以前,入力フォームでアンケートを作成する課題があったが,そのアンケートを集計することを考えよう.このアンケートページは,性別と誕生月と選択肢があるものとする.このアンケートページからCGIに渡されるパラメータは次のようである.

 sex=1&birth=12&select=3

性別の集計

このパラメータを読み取って,次のように性別の集計を行うことができる.

#!/bin/sh
echo Content-type: text/plain
echo
read param
sex=`echo $param |sed 's/.*sex=//;s/&.*//'`
if [ $sex -eq 男性だったときの数字 ] ;then
 男性用カウンタの処理(男性用データファイルからカウント値を読み,+1して,書き戻す)
else
 女性用カウンタの処理(女性用データファイルからカウント値を読み,+1して,書き戻す)
fi

このCGI作成時の注意点は,アクセスカウンタのときと同様である.カウンタ用 のデータファイル(男性用,女性用のふたつとも)の書き込み属性を忘れないこと.

4行目:CGIと入力フォームの連携で説明したように,データを読み込んで変数「param」に格納させる意味がある.

5行目:CGIから渡されてきたパラメータの中から,性別の項を取り出す操作を行っている.このコマンドは,最初に「 `」の中から読んで,echoコマンドによって上の行で読み込んだparamの内容を取り出し,それをsedコマンドに引き渡している.

sex=`echo $param |sed 's/.*sex=//;s/&.*//'`

sedコマンドは,グラフィック表示させたアクセスカウンタの例でも出てきた.詳細は「sed」の説明を見ること.sed コマンドの引数は,最初のsが置換機能を意味し,「/」で囲まれた文字列を次の文字列で置換することを意味している.置換元の文字列として,この例では「.*sex=」が指定されているが,最初の「.」はメタキャラクタであり,どんな1文字にもあてはまるワイルドカードである.次の「*」はそれが繰り返されることを意味し,「.*」ではどんな文字列にでもマッチすることを意味している.つまり「sex=」という文字列が現れるまでは,どんな文字ともマッチしているわけである.この例では置換先の文字列は空のため,行頭から「sex=」となっている部分までを消去する操作を意味する.

それに引き続いて,第二の置換機能が指定されており,今度は置換元が「&.*」と指定されている.つまり,「&」から始まって行末までのすべての文字列にマッチする.これも置換先が空なので,その部分が消去される.第一と第二の両方の置換によって,結局性別を示す数値部分だけが取り出されることになる.

6行目:取り出された性別の値を,「if」コマンドによって判別し,男だったら男性用のカウンタ,女性だったら女性用のカウンタを処理している.ただし,この例は性別を区別する項目の値として,数字が渡されてくる例を示している.もしも,記号が渡されてくる場合,「-eq」ではなく「=」演算子で判断しなければならない.例えば男性の場合に"m",女性の場合"f"が渡されてくるとしたら,6行目は次のように書き換えること.

if [ $sex = "m" ] ;then

具体的なカウンタの処理は省略しているが,それぞれでアクセスカウントと同様な処理を行えばよい.ただしアクセスカウンタの処理とは違い,この集計処理の場合は結果は表示しないでよい.このCGIは表示なしで集計処理だけを行う.つまりアクセスカウンタ用のCGIに存在したechoやprintfコマンドの行は必要ない.実はこのページの最後で説明するように,アンケートに回答してくれたお礼を表示するような別ページにジャンプさせる機能があり,その別ページを表示させるためである.

誕生月の集計

同様に誕生月も集計できる.つまり,まずparamから今度は誕生月の項目を取り出す.

birth=`echo $param |sed 's/.*birth=//;s/&.*//'`

取り出した誕生月を,1月から12月までの12種類別に集計すればよい.しかし性別よりもたくさんの場合分けが必要である.もっとたくさんの条件分けするには,次のように書く.

if [ $birth -eq 1 ]; then
 一月だったときの処理
elif [ $birth -eq 2 ]; then
 二月だったときの処理
elif [ $birth -eq 3 ]; then
...



...
elif [ $birth -eq 12 ]; then
 十二月だったときの処理
fi

elifキーワードは,「else if」の略でif命令を繰り返し使用するために用意されたものである.

ふたつの集計を順番に行うには

性別の集計処理と誕生月の集計処理は,互いに独立した処理ではあるが別々のCGIスクリプトを用意しなくてもよく,一つのCGIスクリプトで順番に処理すればよい.例えば次のようにすればよい.

#!/bin/sh
echo Content-type: text/plain
echo
read param

性別の集計処理の部分

誕生月の集計処理の部分

(さらに次に説明するような記述項目の記録処理などに続く)

記述項目の記録

性別の数や,誕生月などの項目の場合には,種類別に数を集計すればよかった.しかし,記述式の項目の場合には種類が無数存在するために数としては集計できない.記述式の項目は個々のデータをそのまま記録しておく必要がある.

そのまま記録するにしても,アンケートの回答日時も同時に記録しておいた方がよいだろう.回答日時(現在時刻)と,ご意見欄を記録する方法は,次のようにすればよい.

opinion=`echo $param | sed `
date >> 記録用ファイル名
echo "$opinion" >> 記録ファイル名

1行目:記録する項目内容を,性別や誕生月と同様にsedコマンドを用いて取り出す.取り出した項目名が例えばopinionだとする例である.

2行目:現在時刻を記録する.現在時刻を表示するコマンドは「date」コマンドである.「>>」は,ファイルのリダイレクション機能で,コマンドの結果をファイルに追記するためのものである.

記録用ファイルは,カウントデータファイルなどと同様,あらかじめ作成しておく必要がある.空白文字などを入れたファイルを作成し,書き込み属性を付与しておくこと.

3行目で,1行目で取り出した記述項目を記録ファイルに追記する.

実は,記述項目の中に「"」などの文字が混じっているとうまく動作しない可能性があるが,ここではそれをチェックする処理は省略する.

日本語記述に対する注意

ただし,日本語の文章を記録すると,データファイルの中身には文字化けしたように見えるデータが残される.それは正常なので,とりあえずそのままでよい.

CGIに渡されるパラメータはURLエンコードされている.URLエンコードとは,URL中にアルファベットとして表示できない文字を,ある決められたルールによってアルファベット文字に置き換えることをいう.符号化を意味するエンコードに対して,元に戻す処理を意味する言葉がデコード処理である.URLエンコードされた文字は,URLデコード処理をしないと元の日本語には戻らないわけである.今回のCGIスクリプトでは,トラブル避けるため上記のようにそのまま(エンコードされたまま)保存し,結果を表示をするCGIスクリプトの方でデコードすることを考える.

自動ジャンプ

通常のCGIスクリプトでは先頭部分で次のように指定していた.

echo "Content-type: text/html"
echo

これを,次のように変更すると,自動的に指定したページへジャンプさせることができる.

echo Location: http://ジャンプ先のページのURL
echo

上の記述に引き続いて,CGIスクリプトとしての処理を書くことができる.このCGIスクリプトにアクセスした相手のブラウザは,すぐに指定ページへジャンプしてしまうものの,サーバ側では,引き続いてスクリプトの処理を行うことになる.

このような自動ジャンプは,例えばアンケートページの集計処理をしつつ,ユーザにはお礼のページを表示させることなどに使われる.

練習

アンケートの集計CGIを書き換えて,集計作業処理中に,お礼のページを表示するようにせよ.