職案人

求職・歴史・仏教などについて掲載するつもりだが、自分の思いつきが多いブログだよ。適当に付き合って下さい。

アンケート2-(サブルーチン編)

2018年04月08日 | perl
アンケート2-(サブルーチン編)

【環境条件】
Eclipse 4.4(ルナ)
XAMPP 1.8.3 
Perlは既にインストール済み
CGIを使用する時は、必ずApachを起動する

---アンケート2編---

【操作画面】
1)アンケートの質問画面

2)回答画面

3)アンケートの管理画面
パスワードを入れないと表示されない。
ここでは集計結果表示と集計リセットが出来る。


【アンケート2】
前回作ったシンプルなアンケートはenquete-1.html、enquete-2.cgi、enquete-3.cgi の3つのファイルを使っていましたが、今回のアンケート2では1つのファイル CGI で作成し直した。

■スクリプト「enquete.cgi 」
1.#!/usr/local/bin/perl
2.
3.$thisfile = 'enquete.cgi';
4.$datafile = 'enquete.dat';
#パスワード設定
5.$pass = '0123';
6.@ans = ('犬', '猫', 'どちらも嫌い');#配列
7.$ttl = '犬と猫のどちらが好き?';
8.
9.&decode;←サブルーチンを実行する命令
10.if ($in{'mode'} eq 'kaitou') { &kaitou; }
11.elsif ($in{'mode'} eq 'admin') { &admin; }
12.else { &situmon; }
13.exit;
14.
15.#---- フォームデータ取得 ----
16.sub decode {
17. if ($ENV{'REQUEST_METHOD'} eq 'POST') {
18. read(STDIN, $buf, $ENV{'CONTENT_LENGTH'});
19. } else {
20. $buf = $ENV{'QUERY_STRING'};
21. }
22. foreach (split(/&/, $buf)) {
23. ($key, $val) = split(/=/);
24. $in{$key} = $val;
25. }
26.}
27.
28.#---- 質問の表示 ----
29.sub situmon {
30. &html('質問', $ttl, '下から選んでご回答ください');
31. print <<END;
32.<form action="$thisfile" method="post">
33.<input type="hidden" name="mode" value="kaitou">
34.<ul>
35.END
36. for ($i=0; $i<@ans; $i++) {
37. print qq(<li><input type="radio" name="ans" value="$i">$ans[$i]\n);
38. }
39. print <<END;
40.</ul>
41.<input type="submit" value="回答する">\n</form>
42.<form action="$thisfile" method="post">
43.<input type="hidden" name="mode" value="admin">
44.パスワード <input type="password" size="10" name="pw">
45.<input type="submit" value="管理画面">\n</form>
46.</body>\n</html>
47.END
48.}
49.
50.#---- 回答の計数と表示 ----
51.sub kaitou {
52. if (exists $in{'ans'}) {
53. @n = &readData($data);
54. $i = $in{'ans'};
55. $n[$i]++;
56. open(OUT, ">$datafile");
57. foreach (@n) { print OUT "$_\n"; }
58. close(OUT);
59. if (!$data) { chmod(0666, $datafile); }
60. $msg = "あなたは 「$ans[$i]」 の$n[$i]人目です";
61. } else {
62. $msg = "どれかを選んでからお答えください";
63. }
64. &html('回答', 'ご回答の表\示', $msg);
65. &back;
66.}
67.
68.#---- 管理画面 ----
69.sub admin {
70. if (!$in{'pw'}) {
71. $msg = 'パスワードが入力されていません'; $flag=0;
72. } elsif ($in{'pw'} ne $pass) {
73. $msg = 'パスワードが違います'; $flag=0;
74. } elsif ($in{'del'}) {
75. unlink $datafile;
76. $msg = '集計ファイルを削除しました'; $flag=1;
77. } else {
78. $msg = '集計結果'; $flag=1;
79. }
80. &html('集計', '集計結果', $msg);
81. if ($flag == 1) {
82. @n = &readData($data);
83. print "<ul>\n";
84. $i = 0;
85. foreach (@ans) { print "<li>$_ : $n[$i++]\n"; }
86. print "</ul>\n";
87. if ($data) { print <
●●● 解説 ●●●

■サブルーチンについて
・サブルーチン定義
sub サブルーチン名 { ・・・ }

・サブルーチンの呼び出し
&サブルーチン名;

・サブルーチンに引数を渡すには
&サブルーチン名( 引数1, 引数2, ・・・ 引数n );
但し、引数は 「@_ 」 という名前の配列でサブルーチンに渡されます。配列 @_ の添え字は 0 から始まり、最後の添え字は 「$#_ 」 という変数に格納されます。従って、サブルーチンの中では $_[0]、$_[1]、 ・・・ $_[$#_] で、各引数の値を参照したり、逆に各引数の値を設定したりすることができます。また サブルーチンからの戻り値は、サブルーチンの中で return 関数を使って返す
Perlのサブルーチン
例文
# (1)引数に渡す値
my $num1 = 1;
my $num2 = 2;

# (2)サブルーチンの呼び出し
my $total = total($num1, $num2);値は3になる

# (3)サブルーチンの定義
sub total {
my ($num1, $num2) = @_;
my $total = $num1 + $num2;
return $total;
}
戻り値は$totalに代入される。

●●●解説●●●

1)「基本プログラム」

9.&decode;←ルーチンの呼び出し
10.if ($in{'mode'} eq 'kaitou') { &kaitou; }←ルーチンkaitouの呼び出し
11.elsif ($in{'mode'} eq 'admin') { &admin; }←ルーチンadminの呼び出し
12.else { &situmon; }←ルーチンsitumonの呼び出し
13.exit;
14.

9~14行に書かれたプログラムを「基本プログラム」と呼びます。この「基本プログラム」は、いろいろなサブルーチンを呼び出してプログラム全体を進行させ、13行目の exit; でプログラムは終了する。また、いろいろな処理をサブルーチン化しているため、非常に簡潔になっている。

★サブルーチンdecode

#---- フォームデータ取得 ----
16.sub decode {
17. if ($ENV{'REQUEST_METHOD'} eq 'POST') {
#read関数は環境変数 'CONTENT_LENGTH' にセットされた長さの文字列を、STDIN から 変数 $buf に読み込む。
18. read(STDIN, $buf, $ENV{'CONTENT_LENGTH'});
19. } else {
20. $buf = $ENV{'QUERY_STRING'};
21. }
22. foreach (split(/&/, $buf)) {
23. ($key, $val) = split(/=/);
24. $in{$key} = $val;
25. }
26.}
例題の「フォームデータの取得」 の部分をサブルーチンとして纏めたもの。最初に、このサブルーチンを実行し、他のサブルーチンからデータが送られて来た時、そのデータをハッシュ %in に格納する処理をしている。

#---- 質問の表示 ----
sub situmon {
 ・・・
}
#---- 回答の集計と表示 ----
sub kaitou {
 ・・・
}

#---- 管理画面 ----
sub admin {
 ・・・
}

始めはデータ送信されない為、ハッシュ%inは作られない。よって、パスされる。次の10~12行目では、$in{'mode'}=kaitouの場合は、サブルーチンkaitouを呼び出し、$in{'mode'}=adminの場合はサブルーチンadminを呼び出す。それ以外はサブルーチンsitumonを呼び出して、次のexit命令で終了する。

■アンケートの質問画面
サブルーチンsitumonのスクリプト
#---- 質問の表示 ----
29.sub situmon {

サブルーチンhtmlの呼び出し
30. &html('質問', $ttl, '下から選んでご回答ください');

31. print <<END;←ドキュメント
32.<form action="$thisfile" method="post">
33.<input type="hidden" name="mode" value="kaitou">←隠しキー
34.<ul>
35.END
36. for ($i=0; $i<@ans; $i++) {
37. print qq(<li><input type="radio" name="ans" value="$i">$ans[$i]\n);
38. }
39. print </ul>
41.<input type="submit" value="回答する">\n</form>
42.<form action="$thisfile" method="post">
43.<input type="hidden" name="mode" value="admin">
44.パスワード<input type="password" size="10" name="pw">←パスワード入力欄が作成される
45.<input type="submit" value="管理画面">\n</form>
46.</body>\n</html>
47.END
48.}
→→→サブルーチンsitumonの30行で、サブルーチンhtmlを引数を付けて呼び出し、HTMLの前半部分を出力し、31行目以降で2つのフォームを含む HTML の後半を出力している。
32行目の<form action="$thisfile" method="post">で、自分自身を呼び出すように設定している。
36~37行目の for ループで質問に対する回答項目を出力しています。ループ条件を $i<@ans として $ans[$i] を出力するようにしていますが、これは、配列 @ans の内容、要素数を変更しても、ここのスクリプトを変更しないで済むようにしているためです。

以上、実行すると
「犬と猫のどちらが好き?」と言う質問画面を表示される。

★サブルーチンhtml
#---- HTML 書き出し ----
117.sub html {
118.print <<END;←ヒアドキュメント
119.Content-type: text/html\n
120.<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
121.<html>\n<head>
122.<meta content="text/html;">
123.<title>アンケートの$_[0]</title>
124.</head>\n
  <body>
125.<h1>$_[1]</h1>
126.<p><b>$_[2]</b></p>
127.END
128.}
→→→ このサブルーチンsitumonは、CGIヘッダーの2行と HTML の head 部 および body 部の途中までを出力している。また、渡される3つの引数を($_[0]→質問、$_[1]→犬と猫のどちらが好き?、$_[2]→下から選んでご回答ください)で参照して、それらの文字列を 123, 125, 126行目で使用している。

■ 回答画面
このサブルーチンは、回答された数を計数してから、回答内容を表示する画面を出力します。また、このサブルーチン内で、「readData サブルーチン」、「html サブルーチン」、「back サブルーチン」 の3つのサブルーチンを呼び出してる。

サブルーチンkaitouのスクリプト
#---- 回答の計数と表示 ----
51.sub kaitou {
52. if (exists $in{'ans'}) {
53. @n = &readData($data);
54. $i = $in{'ans'};
55. $n[$i]++;
56. open(OUT, ">$datafile");#書き込みモード
57. foreach (@n) { print OUT "$_\n"; }
58. close(OUT);
59. if (!$data) { chmod(0666, $datafile); }
60. $msg = "あなたは 「$ans[$i]」 の$n[$i]人目です";
61. } else {
62. $msg = "どれかを選んでからお答えください";
63. }
64. <font color="red">&html('回答', 'ご回答の表\示', $msg); </font>
65. &back;
66.}
→→→53行目の@n = &readData($data); で、データファイルを読み取り、回答数を@n で受け取っています。引数 $data には、データファイルが存在すれば真、存在しなければ偽がセットされます。

★サブルーチンreadData
99.#---- 集計データ読み取り ----
100.sub readData {
101. my @num; #myはローカルでのみ有効
102. $_[0] = -e $datafile;
103. if ($_[0]) {
104. open(IN, $datafile); #読み込みモード
105. while (<IN>) {
106. chomp;
107. push(@num, $_); #配列にデータを代入
108. }
109. close(IN);
110. } else {
111. for (my $i=0; $i<@ans; $i++) { $num[$i] = 0; }
112. }
113. return @num;
114.}
→→→102行目でファイルテスト演算子 -e を使って $datafile が存在するかどうかを調べる。データファイルが存在すれば真を、存在しなければ偽をセットする。
・ファイルが存在した場合
このファイルの内容を読み取り、これまでのデータを配列numに格納します
・データファイルが存在しない場合
111行目の for ループで @ans と同じ要素数をもつ @num の各要素を0に設定して、これを戻り値とする。
★サブルーチンback
130.#---- 戻るボタン ----
131.sub back {
132. print <<END;
133.<form action="$thisfile">
134.<input type="submit" value=" 戻る ">\n</form>
135.</body>\n</html>
136.END
137.}

■ 管理画面
管理画面は、前回の「集計結果の表示画面」 に類似の画面ですが、管理者だけがアンケートの集計を見ることができ、必要なときには集計をリセットすることができるようにした画面です。集計をリセットする機能は前回にはありませんでしたが、この例題で追加しました。「集計リセット」 というボタンを設け、これが押されたときにデータファイルを削除してしまいます。

#---- 管理画面 ----
69.sub admin {
70. if (!$in{'pw'}) {
71. $msg = 'パスワードが入力されていません'; $flag=0;
72. } elsif ($in{'pw'} ne $pass) {
73. $msg = 'パスワードが違います'; $flag=0;
74. } elsif ($in{'del'}) {
75. unlink $datafile; #ファイルの削除
76. $msg = '集計ファイルを削除しました'; $flag=1;
77. } else {
78. $msg = '集計結果'; $flag=1;
79. }
80. &html('集計', '集計結果', $msg);

81. if ($flag == 1) {
82. @n = &readData($data);
83. print "<ul>\n";
84. $i = 0;
85. foreach (@ans) { print "<li>$_ : $n[$i++]\n"; }
86. print "</ul>\n";
87. if ($data) { print <

93.END
94. }
95. }
96. &back;#サブルーチンbackの呼び出し
97.}
→→→70~79行目で、この admin サブルーチンがどのような状態で呼び出されたのかを調べています。パスワードが正しく入力されていなければ、何らかのメッセージを表示して 「戻る」 ボタンで最初の画面に戻ってもらいます。この状態を $flag=0; として82~94行目の処理がスキップされる。

→→→「集計リセット」 ボタンが押された直後でなければ、78行目が行われた後に82~94行目の処理に進みます。82行目でデータが読み出され、83~86行目の処理でデータを表示します。若しデータファイルが存在すれば、ヒアドキュメントで88~92行目で 「集計リセット」 ボタンのあるフォームを出力します。

このフォームには隠しコントロールを3つ設け、「集計リセット」ボタンが押されたときに enquete.cgi が mode=admin、pw=$pass、del=1 というデータを伴って起動されるようにしておきます。従って、「集計リセット」 ボタンが押された直後には、再びこの admin サブルーチンが呼び出されて75行目にある unlink $datafile; が実行されます。

以上










コメント    この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« シンプルなアンケートを作る | トップ | リンクカウンター/perlのCGI講座 »
最新の画像もっと見る

コメントを投稿

perl」カテゴリの最新記事