CGI-Perl 实例起步

网际网路_线上CGI教室

为了让网友对CGI的写作有更深一层的认识本站特辟此单元来对CGI作实务上的解说,

第一部份我们先以下面的的CGI程式作例子,然後将其程式码逐条拿出来分析测试 , 以

达到立竿见影的效果

线上测验篇

第一题、现任的台北市长是谁?

陈水扁 黄大洲 苏贞昌

第二题、 美国国庆日几月几日?

6月8日 9月4日 7月4日

第叁题、请问有"诗仙"之称的诗人是?

杜甫 白居易 李白

第四题、现任的台北县长是谁?

陈水扁 黄大洲 苏贞昌

第五题、"Superstition"代表下列何种含意?

自信 迷信 守信

当你测试完以上的表格CGI会作出以下的回应

线上测验

你是第 19550 位的受测者

我是来自於:127.0.0.1
测试时间:1998年5月09日23时:03分:39秒

第 1 题你所答的答案是:A 标准答案:A
第 2 题你所答的答案是:A 标准答案:C
第 3 题你所答的答案是:C 标准答案:C
第 4 题你所答的答案是:B 标准答案:C
第 5 题你所答的答案是:B 标准答案:B

您总共答对:3 题;答错:2 题

您总共得到:60 分

也许你会想这是怎麽发生的呢?别忙 ! 让我们看看以下程式码解说後,琢磨之後相信你对

CGI的写作就不会再那麽的陌生了.

# Tony lin Copyright 1998 all rights reserved

# http://www.tpe.wownet.net/~g65617

$|=1;

print "Content-type: text/html\n\n";

$today_date=&get_date;

$counter="counter/counter.txt";


print "<TITLE>The Scripts : WWWQuiz</TITLE>";
print "<h1>线上测验</h1>";
print "<BODY BGCOLOR="#ffffff">";

open(NUMBER,"$counter") || die $!;
$number = <NUMBER>;
close(NUMBER);

$number++;

open(NUMBER,">$counter") || die $!;
print NUMBER $number;
close(NUMBER);

print "你是第 $number 位的受测者<p>";

print"我是来自於:$ENV{'REMOTE_ADDR'}<br>";

print"测试时间:$today_date <p>";

&parse;

%ANSWER=("1","A","2","C","3","C","4","C","5","B");
$right=0;
$wrong=0;

foreach $Num (sort keys %FORM){
print "第 $Num 题你所答的答案是:$FORM{$Num} ";
print "标准答案:$ANSWER{$Num}<br>";

if ($FORM{$Num} eq $ANSWER{$Num}){
$right++;
}else{$wrong++;}
}
$score=20*$right;
print "<p>您总共答对:$right 题;答错:$wrong 题<p>";
print "<p>您总共得到:$score 分<p>";

sub get_date
{

local($date,$localtime);

($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);

if ($sec < 10) {
$sec = "0$sec";
}
if ($min < 10) {
$min = "0$min";
}
if ($hour < 10) {
$hour = "0$hour";
}
if ($mon < 10) {
$mon = "0$mon";
}
if ($mday < 10) {
$mday = "0$mday";
}

$month = ($mon + 1);

$localtime = "19$year年$month月$mday日$hour时\:$min分\:$sec秒";
}

sub parse{

read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});

@pairs = split(/&/, $buffer);

foreach $pair (@pairs){
($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$FORM{$name} = $value;
}
}

研读完上面的原始程式码之後 , 你是不是有点懂又不太懂呢,或是雾煞煞呢 ?

不过没关系,以下的说明相信能让你茅塞顿开,而对它产生了一丁点的兴趣呢

现在我们就把上面的程式码穿肠剖肚的拿出来一一的加以说明吧 !

解说开始______________________________________________解说开始

# Tony lin Copyright 1998 all rights reserved

在perl语言里程式若遇到前面有 # 这样的标示时程式会将它当成注解而不执行它

$|=1;

将$|这个变数设成 1时,就会把缓冲区关毕,这样所输出的资料就不会存在缓冲区里

CGI在输出文字时才不会出错,一般我们都把 $| 设定成 1.

print "Content-type: text/html\n\n";

这一行是告诉CGI程式要以啥格式来输出表单填写後的的回应,因为我们是以HTML

的格式输出所以 Content-type:之後 就以 text/html 这个格式表示之,至於後面的\n\n又

是甚麽意思呢?那是代表换行的字元 .

$today_date=&get_date;

$today_date 这是一个变数 , 甚麽是变数呢 , 我们以下面的例子来作简单的说明 :

$Tony="林立弦" 这时後 如果我们用 print 这个函数来输出 $Tony 这个变数时,那麽

程式码应该会写成这样 :

print $Tony ;

执行之後在你的浏 器上就会出现 :

林立弦 这样的字眼

如果 $Tony="我想我已经了解变数的意义了" 这时後 如果我们用 print 这个函数

来输出 $Tony 这个变数时,执行之後在你的浏 器上就会出现 :

我想我已经了解变数的意义了 这样的字眼

说到这里,我想各为网友已经了解变数的意义了吧 !

那麽 $today_date=&get_date; 又是甚麽涵意呢?

首先我们来谈谈 &get_date 这个东东 ,它是从那里来的呢 ? 相信细心一点的网

友大慨已经了解一二了,是的,它就是从以下这个副程式而来的

sub get_date
{

local($localtime);

($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);

if ($sec < 10) {
$sec = "0$sec";
}
if ($min < 10) {
$min = "0$min";
}
if ($hour < 10) {
$hour = "0$hour";
}
if ($mon < 10) {
$mon = "0$mon";
}
if ($mday < 10) {
$mday = "0$mday";
}

$month = ($mon + 1);

$localtime = "19$year$month$mday$hour\:$min\:$sec";
}

好的 , 让我们来解释一下 &get_date 为什麽是 &get_date 而不是 &Time 或是其它呢

原因很简单 请你注意 sub get_date {}这个叙述, sub 後面所跟的名字是 get_date,所

以我们就以 &这个符号来连结这个副程式的名字,我们之所以取名为 get_date只是

因为这一段 sub get_date {}副程式,我们所要取得的是时间,所以用 get_date来做为

它的名称,这样比较容易了解 ,当然你也可以用其它名称的.

接下来我们再来讨这个 sub get_date {} 两个大刮号里面的叙述到底藏着什麽玄机

, 为何写下了一段 $today_date=&get_date;

然後 载用 print "测试时间:$today_date " ; 你的浏 器就会输出 :

测试时间:199850923:03:39 这些字眼 , 请听下面的分解 :

local($localtime); 意思是把这个 $localtime 变数宣告成 区域变数,什麽是区域变数

我们以後再作解释 .

($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);

localtime(time); 这个函数可以传回 9 个有关时间的元素如 :

$sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst

$sec 代表 秒 , $min 代表 分, $hour 代表 小时 , $mon 代表 月, $mday代表 日, $year 代表 年.

if ($sec < 10) {
$sec = "0$sec";
}
if ($min < 10) {
$min = "0$min";
}
if ($hour < 10) {
$hour = "0$hour";
}
if ($mon < 10) {
$mon = "0$mon";
}
if ($mday < 10) {
$mday = "0$mday";
}

以上这些我们以 if ($min < 10) {$min = "0$min";} 为例 它的意思是说 如果 $min 这个变数

小於 10 的时候 就在 $min 这个变数前面加上一个 0 上去 , if ($min < 10) 是它的条件 ,

{$min = "0$min";} 大括号里面的内容是它的叙述 .

所以当你 的程式写成 这样时 print "测试时间:$today_date " ; 你的浏 器就会回应 :

测试时间:199850923:03:39 这些字眼.

$month = ($mon + 1);

$month 代表 月份 因为它 从 0~11 所以要加 1 才能符合真正的月份

$localtime = "19$year年$month月$mday日$hour时\:$min分\:$sec秒";

这个时候 $localtime 变数会等於 1998年 某月 某日 某时 某分 某秒 (会按系统时间来显示)

当 $localtime 这个变数取得系统时间後 , 会将这个值传回l local($localtime);这个区域变数

所以当我们把程式写成 $today_date=&get_date; 这时 $today_date就等於 $localtime,所以

当你 print $today_date ; 你的浏 器就会回应 "199850923:03:39" 等字眼

$counter="counter/counter.txt";

(注) counter.txt 里面只有放置一组数字来作计数之用

当你写成 $counter ="counter/counter.txt"; (以Win95系统而言)如果你的WWW 伺服器是是安

装在 名为Website这个目录 那代表你的 counter.txt这个档案是放在 Website这目录底下counter

这个子目录里头

如果你是在 Unix系统那 counter.txt 的路径就要写成绝对路径 , 如 $counter="/home/g65617

/counter/counter.txt";这代表在 Unix 系统下 我的目录是 g65617 然後我又在 g65617 底下

设一个子目录 counter 再把 counter.txt 放进去 counter这个子目路录里

有了放 counter.txt 这个档案的路径之後,我们要如何去开启档案来使用呢,很简单,我们用

下面这一段程式码来说明

open(NUMBER,"$counter") || die $!;
$number = <NUMBER>;
close(NUMBER);

$number++;

print NUMBER $number;
close(NUMBER);

open(NUMBER,"$counter")||die $!; 这一段就是要开启档案程式的写法 , 用 open() 来开启 $counter

这个路径的 counter.txt 这个档案 , ||die $! 代表如果开档失败会出现错误讯息 ,NUMBER 是一个

filehandle 它把counter.txt里面的资料内容存放在 $number这个变数里 .

$counter++就是把 counter.txt 档案里面的数字拿出来加 1 之後再用 open(NUMBER,">$counter")

|| die $!;将档案重新开启 , 再用 > 这个符号将 counter.txt 改写 (如果 counter.txt 本来存放的数字

为 5 在 $counter++改写了以後 就变成了 6 ) , 最後记的用 close () 这个函数把档案关掉.

print "你是第 $number 位的受测者<p>";

print"我是来自於:$ENV{'REMOTE_ADDR'}<br>";

print"测试时间:$today_date <p>";

$ENV{'REMOTE_ADDR'}是一个环境变数 , 当你用 print"我是来自於:$ENV{'REMOTE_ADDR'}";

在浏 器就会回应你的 IP 位址

%ANSWER=("1","A","2","C","3","C","4","C","5","B");

%ANSWER 是一个关联阵列 (以本表单为例 , 第一题答案是 A , 第二题答案是 C)一共有五题,

所以我们的关联阵列写成 %ANSWER=("1","A","2","C","3","C","4","C","5","B");

我们用关联阵列 %ANSWER=("1","A","2","C","3","C","4","C","5","B");来做成标准答案的时候

我们又如何让使用者所送出表单上的答案来跟标准答案做一一的比对呢 ?请看下面这一段的说明

$right=0;
$wrong=0;

foreach $Num (sort keys %FORM)

{
print "第 $Num 题你所答的答案是:$FORM{$Num} ";
print "标准答案:$ANSWER{$Num}<br>";

if ($FORM{$Num} eq $ANSWER{$Num}){
$right++;
}else{$wrong++;}
}

$score=20*$right;
print "<p>您总共答对:$right 题;答错:$wrong 题<p>";
print "<p>您总共得到:$score 分<p>";

首先来看 foreach $Num (sort keys %FORM) , 我们用 keys 这个函数将FORM的每个KEY

取出存成一个阵列,再用 sort 这个函数来排序 (由小排到大)这时候 $Num 所存放的是表单

里 name 的值 . $FORM{$Num}所存放的是表单里 value 的值 (请各别参照表单的原始档).

$ANSWER{$Num}所存放的是 %ANSWER=("1","A","2","C","3","C","4","C","5","B");

这个关联阵列由 $Num 所对应出来 A,C,C,C,B .

我们运用 foreach 这个回圈 功能 从第一题到第五题 取出阵列里的元素,再来比对使用者

所送出的答案.

if ($FORM{$Num} eq $ANSWER{$Num}){
$right++;
}else{$wrong++;}
}
这一段 读成 如果 ($FORM{$Num} (也就是表单里 value 的值 ) eq (等於)$ANSWER{$Num}

(也就是我们所设定的标准答案 %ANSWER=("1","A","2","C","3","C","4","C","5","B");

的 A,C,C,C,B)

$right++; 意思是 $right 这个变数加 1 的意思原本我们设定 $right=0 , 当答对 1 题时 $right

这个变数就会 + 1 再答对 1 题时 $right 这个变数就会再 + 1

else{$wrong++;} 意思是 : 如果答错 $wrong 这个变数自动加 1

$score=20*$right;
print "<p>您总共答对:$right 题;答错:$wrong 题<p>";
print "<p>您总共得到:$score 分<p>";

最後我们再设定一个变数 $score 把每题20分 乘以 答对的题数 存在 $score 里面 ,再用 print 这个

函数 ,将它 输出到浏 器上面来, 於是程式应该写成

print "<p>您总共答对:$right 题;答错:$wrong 题<p>";
print "<p>您总共得到:$score 分<p>";

以上所述就是 表单与 CGI 应用程式间的互动,下次我们将介绍一些 Perl 的常用的语法,敬请期待!