CGI编程入门与实战:从基础到应用
1. CGI编程基础
在CGI编程中,使用
CGI.pm
模块可以方便地生成HTML页面。传统的调用方式可能会有较多的
print()
函数调用,而更推荐将多个方法组合在一个
print()
函数中,这样代码更具可读性。
例如,
cgi2.pl
程序展示了这种方式:
#!/usr/bin/perl
# cgi2.pl
# this program generates HTML with the use
# of CGI.pm using the conventional style
use warnings;
use strict;
use CGI ':standard';
print
header(),
start_html('Generating HTML'),
h1('Now Is:'),
p('The current date and time is:', scalar(localtime)),
hr(),
h1('Our CGI Scripts');
my $file_listing = '';
$file_listing .= "<br />$_" foreach <*.pl>;
print
p('By the time this chapter is over, you will write all of',
'these scripts:', $file_listing),
h1('Go Here For Excellent Books!'),
p('Check out the',
a({ href => 'http://www.apress.com/' }, 'Apress Home Page')),
end_html();
需要注意的是,在使用这种方式时,方法之间要用逗号分隔,若使用分号可能会导致后续方法无法输出。
2. CGI.pm方法详解
CGI.pm
模块的方法可以分为生成多个标签和生成单个标签两类。
-
生成多个标签的方法
:如
start_html()
,它会生成
<html><head><title>...</title></head><body>
等标签。可以使用命名参数来设置页面的属性,例如:
start_html(
-title => 'Generating HTML',
-bgcolor => '#cccccc',
-text => '#520063'
),
-
生成单个标签的方法
:像
h1()、p()、hr()等,它们分别生成<h1>...</h1>、<p>...</p>、<hr />标签。还可以通过传递匿名哈希来为标签提供属性,示例如下:
h1({ align => 'center'}, 'Here Is An Example Web Page'),
hr({ size => 10, noshade => 1 }),
a({ href => 'http://www.apress.com/' }, 'Apress Home Page'),
3. 处理表单数据
CGI脚本不仅能生成HTML,还能进行后端处理,其中一个重要的应用就是处理表单数据。表单由
<form>
和
</form>
标签包围,
<form>
标签的
action
属性指定了用户提交表单时要调用的CGI脚本。
以下是一个简单的表单示例
form.html
:
<html>
<head>
<title>A Simple Form</title>
</head>
<body>
<h1>Please Enter Your Name</h1>
<form action="http://localhost/cgi-bin/form.pl">
First name: <input type="text" name="firstname">
<br>
Last name: <input type="text" name="lastname">
<br>
<input type="submit">
</form>
</body>
</html>
对应的CGI程序
form.pl
使用
param()
方法来获取表单数据:
#!/usr/bin/perl
# form.pl
use warnings;
use strict;
use CGI ':standard';
my @params = param();
my $firstname = param('firstname') || 'you have no first name!';
my $lastname = param('lastname') || 'you have no last name!';
print
header(),
start_html(
-title => 'Welcome!',
-text => '#520063'
),
h1("Hello, $firstname $lastname!"),
end_html();
param()
方法有两种调用方式:
- 无参数调用时,返回表单中所有参数的列表。
- 带一个参数调用时,返回该参数的值。
4. 动态CGI程序
静态CGI程序将HTML和CGI程序分开存储,有时管理起来较为困难。动态CGI程序则可以根据是否有表单数据来决定是处理数据还是构建表单。
动态CGI程序的一般形式如下:
if (param()) {
# the program was invoked with parameters,
# process the posted data
} else {
# the program was invoked with no parameters,
# build the form
}
以下是一个动态CGI程序
dynamic.pl
的示例:
#!/usr/bin/perl
# dynamic.pl
use warnings;
use strict;
use CGI ':standard';
if (param()) {
# we have parameters, so process the form data
my @params = param();
my $firstname = param('firstname') || 'you have no first name!';
my $lastname = param('lastname') || 'you have no last name!';
print
header(),
start_html(
-title => 'Welcome!',
-text => '#520063'
),
h1("Hello, $firstname $lastname!"),
end_html();
} else {
# no parameters, so build the form
print
header(),
start_html('A Simple Form'),
h1('Please Enter Your Name'),
start_form(),
'First name: ',
textfield(-name => 'firstname'),
br(),
'Last name: ',
textfield(-name => 'lastname'),
br(),
submit(),
end_form(),
end_html();
}
5. 实战:Web象棋程序
接下来,我们将前面讨论的内容整合到一个Web象棋程序
webchess.pl
中。由于CGI脚本是无状态的,我们需要将棋盘状态存储在
webchess.dat
文件中。
以下是棋盘的初始状态:
WR:WN:WB:WQ:WK:WB:WN:WR
WP:WP:WP:WP:WP:WP:WP:WP
:::::::
:::::::
:::::::
:::::::
BP:BP:BP:BP:BP:BP:BP:BP
BR:BN:BB:BQ:BK:BB:BN:BR
webchess.pl
程序的主要流程如下:
1. 读取
webchess.dat
文件中的棋盘状态。
2. 获取用户提交的起始和结束坐标。
3. 根据坐标移动棋子,并更新棋盘状态。
4. 将更新后的棋盘状态写回
webchess.dat
文件。
5. 输出HTML页面,显示棋盘和表单。
以下是
webchess.pl
的完整代码:
#!/usr/bin/perl
# webchess.pl
use warnings;
use strict;
use CGI ':standard';
my @chessboard = read_in_chessboard();
# grab the posted data, if any:
my $start = param('start') || '';
my $end = param('end') || '';
my $startx = '';
my $starty = '';
my $endx = '';
my $endy = '';
# time to make our move!
if ($start and $end) {
if ($start =~ /^\s*([1-8]),([1-8])/) {
$startx = $1 - 1;
$starty = $2 - 1;
}
if ($end =~ /^\s*([1-8]),([1-8])/) {
$endx = $1 - 1;
$endy = $2 - 1;
}
if ($startx ne '' and $starty ne '' and
$endx ne '' and $endy ne '' ) {
# put starting square on ending square
$chessboard[$endy][$endx] = $chessboard[$starty][$startx];
# remove from old square
undef $chessboard[$starty][$startx];
# we have changed the chessboard, so write
# back out
write_out_chessboard(@chessboard);
}
}
# time to print to the browser
print
header(),
start_html('Web Chess'),
h1('Web Chess');
# start the table that will contain the board
print '<table>';
# loop, printing each piece
foreach my $i (reverse (0..7)) { # row
print '<tr>';
foreach my $j (0..7) { # column
print '<td>';
if (defined $chessboard[$i][$j]) {
print $chessboard[$i][$j];
} elsif ( ($i % 2) == ($j % 2) ) {
print "..";
}
print '</td>';
}
print "</tr>"; # end of row
}
# we are done with our table
print '</table>';
# print a form for the next move
# and end the html
print
hr(),
start_form(),
'Starting square [x,y]:',
textfield(-name => 'start'),
br(),
'Ending square [x,y]:',
textfield(-name => 'end'),
br(),
submit(),
end_form(),
end_html();
### function definitions ###
sub read_in_chessboard {
# this function opens webchess.dat and builds
# the chessboard
# an example line from webchess.dat is:
# BR:BN:BB:BQ:BK:BB:BN:BR
# this is our local copy of the chessboard,
# we'll return this later
my @cb;
open FH, '<', 'webchess.dat';
foreach my $i (0..7) {
my $line = <FH>;
# split the line on a : or any whitespace
# which will take care of the \n at the
# end of the line
my @linearray = split /[:\s]/, $line;
# $#linearray should be 7!
foreach my $j (0..$#linearray) {
# if the text between the colons is
# not the empty string, we have a piece,
# so assign it to our chessboard
if ($linearray[$j]) {
$cb[$i][$j] = $linearray[$j];
}
}
}
close FH;
# time to return back the chessboard
return @cb;
}
sub write_out_chessboard {
# the chessboard is passed in as our
# argument
my @cb = @_;
# write the chessboard to webchess.dat
# so that each piece on a row is colon separated
open FH, '>', 'webchess.dat';
foreach my $i (0..7) {
foreach my $j (0..7) {
if (defined $chessboard[$i][$j]) {
print FH $chessboard[$i][$j];
}
if ($j < 7) {
print FH ':';
}
}
print FH "\n";
}
}
总结
通过以上内容,我们了解了CGI编程的基础知识,包括
CGI.pm
模块的使用、表单数据处理、动态CGI程序的实现,以及如何将这些知识应用到实际项目中。希望这些内容能帮助你更好地掌握CGI编程。
操作步骤总结
| 操作 | 步骤 |
|---|---|
使用
CGI.pm
生成HTML
|
1. 引入
CGI.pm
模块。
2. 使用相关方法生成HTML标签。 3. 可以将多个方法组合在一个
print()
函数中。
|
| 处理表单数据 |
1. 创建包含表单的HTML文件。
2. 在CGI程序中使用
param()
方法获取表单数据。
|
| 实现动态CGI程序 |
1. 根据
param()
方法的返回值判断是否有表单数据。
2. 有数据则处理数据,无数据则构建表单。 |
| 运行Web象棋程序 |
1. 准备
webchess.dat
文件,存储棋盘初始状态。
2. 运行
webchess.pl
程序。
3. 在浏览器中访问程序,输入起始和结束坐标进行下棋。 |
流程图
graph TD;
A[开始] --> B[读取棋盘状态];
B --> C[获取用户输入];
C --> D{是否有输入};
D -- 是 --> E[处理输入,更新棋盘];
D -- 否 --> F[直接显示棋盘];
E --> G[更新棋盘文件];
G --> F;
F --> H[输出HTML页面];
H --> I[结束];
CGI编程入门与实战:从基础到应用
6. 代码详细解析
6.1
read_in_chessboard
函数
sub read_in_chessboard {
# this function opens webchess.dat and builds
# the chessboard
# an example line from webchess.dat is:
# BR:BN:BB:BQ:BK:BB:BN:BR
# this is our local copy of the chessboard,
# we'll return this later
my @cb;
open FH, '<', 'webchess.dat';
foreach my $i (0..7) {
my $line = <FH>;
# split the line on a : or any whitespace
# which will take care of the \n at the
# end of the line
my @linearray = split /[:\s]/, $line;
# $#linearray should be 7!
foreach my $j (0..$#linearray) {
# if the text between the colons is
# not the empty string, we have a piece,
# so assign it to our chessboard
if ($linearray[$j]) {
$cb[$i][$j] = $linearray[$j];
}
}
}
close FH;
# time to return back the chessboard
return @cb;
}
此函数的主要作用是从
webchess.dat
文件中读取棋盘状态。具体步骤如下:
1. 创建一个本地数组
@cb
,用于存储棋盘状态。
2. 以只读模式打开
webchess.dat
文件。
3. 循环读取文件的每一行,将每行按冒号或空白字符分割成数组
@linearray
。
4. 遍历分割后的数组,如果元素不为空,则将其赋值给
@cb
数组对应的位置。
5. 关闭文件并返回
@cb
数组。
6.2
write_out_chessboard
函数
sub write_out_chessboard {
# the chessboard is passed in as our
# argument
my @cb = @_;
# write the chessboard to webchess.dat
# so that each piece on a row is colon separated
open FH, '>', 'webchess.dat';
foreach my $i (0..7) {
foreach my $j (0..7) {
if (defined $chessboard[$i][$j]) {
print FH $chessboard[$i][$j];
}
if ($j < 7) {
print FH ':';
}
}
print FH "\n";
}
}
该函数的功能是将棋盘状态写回到
webchess.dat
文件中。操作步骤如下:
1. 接收传入的棋盘数组
@cb
。
2. 以写入模式打开
webchess.dat
文件。
3. 双重循环遍历棋盘的每一个位置,如果该位置有棋子,则将棋子信息写入文件。
4. 除每行的最后一个位置外,每个位置后都写入一个冒号。
5. 每行结束后写入换行符。
6.3 主程序逻辑
my @chessboard = read_in_chessboard();
# grab the posted data, if any:
my $start = param('start') || '';
my $end = param('end') || '';
my $startx = '';
my $starty = '';
my $endx = '';
my $endy = '';
# time to make our move!
if ($start and $end) {
if ($start =~ /^\s*([1-8]),([1-8])/) {
$startx = $1 - 1;
$starty = $2 - 1;
}
if ($end =~ /^\s*([1-8]),([1-8])/) {
$endx = $1 - 1;
$endy = $2 - 1;
}
if ($startx ne '' and $starty ne '' and
$endx ne '' and $endy ne '' ) {
# put starting square on ending square
$chessboard[$endy][$endx] = $chessboard[$starty][$startx];
# remove from old square
undef $chessboard[$starty][$startx];
# we have changed the chessboard, so write
# back out
write_out_chessboard(@chessboard);
}
}
# time to print to the browser
print
header(),
start_html('Web Chess'),
h1('Web Chess');
# start the table that will contain the board
print '<table>';
# loop, printing each piece
foreach my $i (reverse (0..7)) { # row
print '<tr>';
foreach my $j (0..7) { # column
print '<td>';
if (defined $chessboard[$i][$j]) {
print $chessboard[$i][$j];
} elsif ( ($i % 2) == ($j % 2) ) {
print "..";
}
print '</td>';
}
print "</tr>"; # end of row
}
# we are done with our table
print '</table>';
# print a form for the next move
# and end the html
print
hr(),
start_form(),
'Starting square [x,y]:',
textfield(-name => 'start'),
br(),
'Ending square [x,y]:',
textfield(-name => 'end'),
br(),
submit(),
end_form(),
end_html();
主程序的执行流程如下:
1. 调用
read_in_chessboard
函数读取棋盘状态。
2. 使用
param
方法获取用户提交的起始和结束坐标。
3. 判断是否有有效的坐标输入,如果有则进行坐标解析和棋盘更新操作。
4. 调用
write_out_chessboard
函数将更新后的棋盘状态写回文件。
5. 输出HTML页面,包括棋盘表格和用于输入下一步棋的表单。
7. 常见问题及解决方法
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 部分页面内容未显示 | 方法调用时使用了分号而非逗号 | 检查代码,将分号替换为逗号 |
| 表单数据无法获取 |
param
方法使用错误
|
确保
param
方法正确调用,检查表单中
name
属性的值
|
| 棋盘状态未更新 | 文件读写操作失败 |
检查文件权限,确保
webchess.dat
文件可读写
|
8. 扩展与优化建议
- 增加错误处理 :在文件读写操作和坐标解析时增加更多的错误处理代码,提高程序的健壮性。例如,在打开文件失败时给出相应的错误提示。
open FH, '<', 'webchess.dat' or die "无法打开文件: $!";
- 优化界面显示 :可以使用CSS样式来美化棋盘和表单的显示效果,提高用户体验。
- 实现更多功能 :如悔棋、保存游戏记录等功能,增强程序的实用性。
9. 总结回顾
本文全面介绍了CGI编程的相关知识,从基础的
CGI.pm
模块使用,到表单数据处理、动态CGI程序实现,最后通过Web象棋程序的实例将这些知识进行了整合应用。通过详细的代码解析、操作步骤说明和流程图展示,帮助读者更好地理解和掌握CGI编程的核心要点。
希望读者在学习过程中能够深入理解每个知识点,并通过实践不断巩固和拓展自己的技能。在实际应用中,根据具体需求对程序进行灵活调整和优化,以实现更强大的功能。
流程图
graph LR;
A[读取棋盘状态] --> B{是否有用户输入};
B -- 是 --> C[解析输入坐标];
B -- 否 --> D[显示当前棋盘];
C --> E[更新棋盘状态];
E --> F[写入棋盘文件];
F --> D;
D --> G[输出HTML页面];
G --> H[等待下一次输入];
这个流程图展示了Web象棋程序的整体运行流程,从读取棋盘状态开始,根据用户是否有输入进行不同的处理,最终输出HTML页面供用户交互。
超级会员免费看
2

被折叠的 条评论
为什么被折叠?



