Perl入门教程

环境安装

基础语法

数据类型

  • 标量
  • 数组
  • 哈希

变量

my声明变量,our可以声明全局变量。

1
2
3
our ($sql, $sql_comment);

my ($table_name, $result);

标量

标量可以是一个整数,浮点数,字符,字符串,段落或者一个完整的网页。

1
2
3
4
5
6
7
# 数字标量
$integer = 200;
$negative = -300;
$floating = 200.340;
$bigfloat = -1.2E-23;
$octal = 0377;
$hexa = 0xff;
1
2
3
4
5
6
7
8
# 字符串标量
$var = "字符串标量 - 菜鸟教程!";
$quote = '我在单引号内 - $var';
$double = "我在双引号内 - $var";
$escape = "转义字符使用 -\tHello, World!";

$str = "hello" . "world"; # 字符串连接
$str = $str . $num; # 字符串与数字连接

特殊字符

__为两条下划线,__LINE____FILE____PACKAGE__分别表示行号、文件名和包名。
特殊字符是单独的标记,不能写在字符串中。

v 字符串

以 v 开头,后面跟着一个或多个用句点分隔的整数,会被当作一个字串文本。

数组

数组一个是存储标量值的列表变量,变量可以是不同类型。数组变量以 @ 开头。访问数组元素使用 $ + 变量名称 + [索引值] 格式来读取。

创建数组

1
2
3
4
5
6
7
8
9
# 用括号内的元素创建数组
@array = (1, 2, 'hello');

# 用qw/.../创建数组,用空格分隔数组元素
@array = qw/这是 一个 数组/;

# 用 start..end 的格式创建连续序列的数组
@var = (1..10);
@var = (a..z);

访问数组元素

  • 索引值从 0 开始,即 0 为第一个元素,1 为第二个元素,以此类推。

  • 负数从反向开始读取,-1 为第一个元素, -2 为第二个元素

1
2
print "$array[0]\n";
print "$array[-1]\n";

获取数组元素数目

数组大小由数组中的标量上下文决定(用标量去访问数组,即可获取数组的大小)。

1
2
3
4
@array = (1, 2, 3);
print "数组大小为", @array "\n";
$size = @array;
$max_idx = $#array;

数组元素的增删

1
2
3
4
5
6
7
8
9
10
11
@sites = ("google","runoob","taobao");
$new_size = @sites ;
# unshift: 在数组开头添加一个元素
$new_size = unshift(@sites, "weibo");
# push: 在数组末尾添加一个元素
$new_size = push(@sites, "baidu");

# pop: 删除数组的最后一个值
$new_byte = pop(@sites);
# shift:删除数组的第一个值
$new_byte = shift(@sites);

数组切片

1
2
@results = @data[3, 4, 5];
@results = @data[3..5];

替换数组元素

1
2
3
4
5
# 语法: @array为待替换的数组,offset为起始替换位置,length为替换的元素个数,list为替换的数组。
splice(@array, offset[, length[, list]])

# 示例
splice(@data, 5, 5, 21..25);

将字符串转为数组

1
2
3
4
5
6
# 语法:pattern为分隔符,expr为指定的字符串数,limit为返回该数组的元素个数。
split(pattern[, expr[, limit]])

# 示例
@var_string = "www.baidu.com";
@test = split('.', $var_string);

将数组转为字符串

1
2
3
4
5
# 语法:expr为连接符,list为数组或列表
join (expr, list)

# 示例
$str = join('.', @strings);

数组排序

1
2
3
4
5
# 语法:subroutine为指定规则
sort[subroutine] list

# 示例
@results = sort(@data);

数组合并

可以用逗号来合并数组。

1
2
@nums = (1, 3, (4, 5, 6));
@nums = (@nums_a, @nums_b);

哈希

哈希是 key/value 对的集合,用百分号标记开始,访问哈希元素的格式为 ${key}。

创建哈希

  • 为每个 key 设置 value
1
2
$data{'google'} = 'google.com';
$data{'amazon'} = 'amazon.com';
  • 通过列表设置
1
2
3
4
5
6
# 列表中第一个元素为 key,第二个为 value。
%data = ('google', 'google.com', 'amazon', 'amazon.com');
# 使用 => 符号来设置key/value
%data = ('google'=>'google.com', 'amazon'=>'amazon.com');
# 用-代替引号(这种方式key不能出现空格)
%data = (-google=>'google.com', -amazon=>'amazon.com');

访问哈希元素

访问哈希元素可以通过 ${key}的形式,也可以以${key1, key2...}的形式将哈希值提取到数组。

1
2
3
$google = $data{'google'};

@results = @data{'google', 'amazon'};

读取哈希值

读取所有的 keys/values 可以通过关键字 keysvalues 加上哈希变量来获取。

1
2
@keys = keys %data;
@values = values %data;

检查元素是否存在

使用 exists 函数来判断 key 是否存在.

1
2
3
if (exists($data{'facebook'})){
print "facebooke的网址为 $data{'facebook'}\n"
}

获取哈希元素数目

可以通过先获取 key 或 value 的所有元素数组,再计算数组元素多少来获取哈希的元素个数。

1
2
3
4
5
@keys = keys %data;
$size = @keys;

@values = values %data;
$size = @values;

哈希元素的增删

添加哈希元素可以直接通过 key/value 的赋值完成,删除元素需要使用 delete。

1
2
3
4
5
# 添加元素
$data{'alibaba'} = 'alibaba-inc.com';

# 删除元素
delete $data{'baidu'};

哈希的迭代遍历

哈希的遍历用 foreach 和 while 来实现。

1
2
3
4
5
6
7
8
9
# 使用foreach
foreach $key (keys %data){
print "$data{$key}\n"
}

# 使用while
while ($key, $value) = each(%data){
print "$data{$key}\n"
}

条件语句

数字 0、字符串’0’、””、空 list()、undef 为 false,其余为 true。

if 系列

  • if
  • if… else
  • if…elsif…else
    • elsif 后面必须有 else 语句;
    • 如果其中的一个 elsif 执行成功,其他的 elsif 和 else 将不再被执行
1
2
3
4
5
6
7
8
9
10
11
# 示例
$a = 10;
if($a < 20){
printf "a 小于 20";
}
elsif($a < 30){
printf "a 介于20和30之间";
}
else{
printf "a 大于 30";
}

unless 系列

  • unless

  • unless…else

  • unless…elsif…else

    • unless 的条件为 false 时,执行语句;为 true 时,不执行。unless 理解为“除非”,“除非满足 xxx 条件,否则就要执行 xxxx 语句”。
1
2
3
4
5
6
7
8
9
10
11
$a = 20;
unless( $a < 20 ){
# 布尔表达式为 false 时执行
printf "a 大于等于 20\n";
}
elsif($a < 10){
printf "a 介于10和20之间";
}
else{
printf "a 小于10";
}

switch 系列

  • switch case 执行是基于 Switch 模块, Switch 模块默认是没有安装的。
  • switch 语句的括号中可以使用任意类型的标量参数。
  • 在一个 switch 中可以有任意数量的 case 语句。每个 case 后跟一个要比较的值和一个冒号。
  • case 语句后的标量会与 switch 语句的标量进行比较,判断是否相等。 -当被测试的变量等于 case 中的常量时,case 后跟的语句将被执行,直到遇到 break 语句为止。
  • switch 语句可以有一个可选的 else ,该语句在最后面,该语句在所有 case 不匹配的情况下执行。
  • 当匹配 case 后,会执行 case 语句块代码,执行后跳出 switch 语句。
  • 当匹配 case 后,如果我们需要继续执行接下来的 case 语句,则需要添加 next 语句。
1
2
3
4
5
6
7
8
9
10
11
12
13
use Switch;

$var = 10;
@array = (10, 20, 30);
%hash = ('key1' => 10, 'key2' => 20);
switch($var){
case 10 {print "数字10\n"; next;}
case 'a' {print "String a";}
case [1..10, 42] {print "数字在列表中";}
case (\@array) {print "数字在数组中";}
case (\%hash) {print "数字在哈希中";}
else {print "没有匹配的条件";}
}

?: 系列

1
2
3
4
5
# 基本语法
expr1? conditional_true : conditional_false;

# 示例
$status = ($favorite > 60)? "Y":"N";

循环

while/do…while 循环

1
2
3
4
5
6
7
8
9
10
11
12
$a = 10;

# while 循环
while ($a < 20>){
$a = $a + 1;
}

# do...while 循环
do{
printf "a 的值为: $a\n";
$a = $a + 1;
}while( $a < 15 );

until 循环

until 语句在给定条件为 false 时,重复执行语句或语句组。

1
2
3
4
5
6
7
$a = 5;

# 执行 until 循环
until( $a > 10 ){
printf "a 的值为 : $a\n";
$a = $a + 1;
}

for / foreach 循环

foreach 语法为 foreach var (list){…}

1
2
3
4
5
6
7
8
9
10
11
# for 循环
for( $a = 0; $a < 10; $a = $a + 1 ){
print "a 的值为: $a\n";
}


# 执行foreach 循环
@list = (2, 12, 36, 42, 51);
foreach $a (@list){
print "a 的值为: $a\n";
}

循环控制语句

  • next
    • 等价于 python 里的 continue。
  • last
    • 等价于 break。
  • continue
    • 用于 while 和 foreach 循环,常用于下一次条件判断之前(等价于循环体内的最后一部分)。
  • redo
    • 与 next 类似,但是不会再次执行判断,也不会执行 continue 模块里的语句。
  • goto
    • 等价于 php 里的 goto。

无限循环

1
2
3
for (;;){
printf "无限循环\n";
}

运算符

算术运算符

1
2
3
4
5
6
$a + $b  # 加法
$a - $b # 减法
$a * $b # 乘法
$a / $b # 除法
$a % $b # 取余数
$a ** $b # 乘幂

比较运算符

1
2
3
4
5
6
7
8
# 数字的大小比较
$a == $b # 是否相等
$a != $b # 是否不等
$a <=> $b # 比较($a>$b则返回1,$a==$b则返回0,否则返回-1)
$a > $b # 是否大于
$a >= $b # 是否大于等于
$a < $b # 是否小于
$a <= $b # 是否小于等于
1
2
3
4
5
6
7
8
# 字符串的大小比较
$a lt $b # 是否小于
$a le $b # 是否小于等于
$a gt $b # 是否大于
$a ge $b # 是否大于等于
$a eq $b # 是否等于
$a ne $b # 是否不等于
$a cmp $b # 比较($a>$b则返回1,$a==$b则返回0,否则返回-1)

逻辑运算符

1
2
3
4
5
$a and $b   # 逻辑与
$a && $b # 逻辑与
$a or $b # 逻辑或
$a || $b # 逻辑或
not $a # 逻辑非

赋值运算符

1
2
3
4
5
6
7
$a = $b  # 简单赋值
$a += $b # 加等于
$a -= $b # 减等于
$a *= $b # 乘等于
$a /= $b # 除等于
$a %= $b # 模等于
$a **= $b # 幂等于

位运算符

1
2
3
4
5
6
$a & $b  # 位与
$a | &b # 位或
$a ^ $b # 位异或
~ $a # 位非
$a << 2 # 左移
$a >> 2 # 右移

引号运算符

1
2
3
q{abcd}    # 添加单引号,结果为'abcd'
qq{abcd} # 添加双引号,结果为"abcd"
qx{abcd} # 添加反引号,结果为`abcd`

其他运算符

1
2
3
4
5
6
"hello" . "world"  # 字符串拼接
'-' x 3 # 字符串重复,返回'---'
(2..5) # 范围,结果为(2, 3, 4, 5)
$a++ # 自增
$b-- # 自减
$obj->$a # 类的方法,表示$obj的$a方法

函数

函数定义

1
2
3
sub subroutine{
statements;
}

函数调用

1
2
3
4
subroutine(参数列表);

# perl 5.0用法
&subroutine(参数列表);
  • 设置私有变量,可以使用 my 操作符来设置。
  • 可以使用 local 为全局变量提供临时的值,在退出作用域后将原来的值还回去。
  • state 关键字将局部变量变得持久。state 仅能创建闭合作用域为子程序内部的变量。state 使用前需要use feature 'state';
  • 子程序调用过程中,会根据上下文来返回不同类型的值。

参数传递

  • 接受多个参数
  • 子程序参数使用特殊数组@_标明,因此第一个参数为$_[0],依此类推。
  • 无论参数是标量的还是数组的,用户把参数传给子程序时,perl 默认按照引用的方式调用它。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 定义求平均值函数
sub Average{
# 获取所有传入的参数
$n = scalar(@_);
$sum = 0;

foreach $item (@_){
$sum += $item;
}
$average = $sum / $n;
print '传入的参数为 : ',"@_\n"; # 打印整个数组
print "第一个参数值为 : $_[0]\n"; # 打印第一个参数
print "传入参数的平均值为 : $average\n"; # 打印平均值
}

# 调用函数
Average(10, 20, 30);
  • 如果我们需要传入标量和数组参数时,需要把列表放在最后一个参数上
1
2
3
4
5
6
7
8
9
10
# 定义函数
sub PrintList{
my @list = @_;
print "列表为 : @list\n";
}
$a = 10;
@b = (1, 2, 3, 4);

# 列表参数
PrintList($a, @b)
  • 在传入多个数组和哈希时,会导致丢失独立的标识。所以我们需要使用引用来传递。
  • 当向子程序传递哈希表时,它将复制到 @_ 中,哈希表将被展开为键/值组合的列表。
1
2
3
4
5
6
7
8
9
10
11
12
sub PrintHash{
my (%hash) = @_;

foreach my $key ( keys %hash ){
my $value = $hash{$key};
print "$key : $value\n";
}
}
%hash = ('name' => 'runoob', 'age' => 3);

# 传递哈希
PrintHash(%hash);

返回值

  • 使用 return 语句来返回函数值。
  • 如果没有使用 return 语句,则子程序的最后一行语句将作为返回值。
    可以返回标量,数组和哈希,但是在返回多个数组和哈希时,会导致丢失独立的标识。所以我们需要使用引用来返回多个数组和哈希。

引用

引用就是指针,Perl 引用使一个标量类型可以指向变量、数组、哈希表(也叫关联数组)甚至子程序,可以应用在程序的任何地方。

创建引用

  • 在变量前面加一个\,就得到了一个变量的引用。
1
2
3
4
5
$scalarref = \$foo;  # 标量引用
$arrayref = \@ARGV; # 列表引用
$hashref = \%ENV; # 哈希引用
$coderef = \&handler;# 子过程引用
$globref = \*foo; # glob句柄引用
  • 数组中:可以用[]实现匿名数组的引用,匿名数组的元素仍然可以是匿名数组,从而构造任意维度的数组。
1
2
3
4
5
my $aref = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
  • 哈希中:用{}定义匿名哈希引用
1
$href = {APR =>4, AUG=>8};
  • 子程序中:
1
$coderef = sub {print "hello\n"};

取消引用

根据不同的类型,使用$ @ %来取消。

如果不确定变量类型,可以使用 ref 来判断。ref 方法的返回值包括:SCALAR, ARRAY, HASH, CODE, GLOB, REF,以及 false(以上均不存在)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$var = 10;

# $r 引用 $var 标量
$r = \$var;
print "取消标量引用: ", $$r;

@var = (1, 2, 3);
$r = \@var;
print "取消数组引用: ", @$r;

%var = ('k1' => 1, 'k2' => 2);
$r = \%var;
print "r的引用类型: ", ref($r);
print "取消哈希引用: ", %$r;

循环引用

循环引用在两个引用相互包含时出现。你需要小心使用,不然会导致内存泄露。(不推荐使用)

1
2
3
4
my $foo = 100;
$foo = \$foo;

print "Value of foo is : ", $$foo, "\n";

引用函数

函数引用格式: \&。调用引用函数格式: & + 创建的引用名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 函数定义
sub PrintHash{
my (%hash) = @_;

foreach $item (%hash){
print "元素 : $item\n";
}
}
%hash = ('name' => 'runoob', 'age' => 3);

# 创建函数的引用
$cref = \&PrintHash;

# 使用引用调用函数
&$cref(%hash);

高级语法

格式化输出

使用 format 来定义一个模板,然后使用 write 按指定的模板输出数据。格式化的定义为:

1
2
3
4
5
6
format FormatName =
fieldline
value_1, value_2, value_3
fieldline
value_1, value_2
.
  • FormatName 为格式化名称
  • fieldline:为一个格式行,用来定义一个输出行的格式,类似 @, ^, <, >, | 等。
  • value_1, value_2,…为一个数据行,用来向前面的格式行中插入值。
  • .: 结束符号。

示例:

1
2
3
4
5
6
7
format STDOUT =
first: ^<<<<<< # 左对齐,长度为6
$text
second: ^<<<<< # 左对齐,长度为5
$text
.
write

格式行

  • 以 @ 和 ^ 开头的行,不做任何变量代换。
  • @ 字段时普通的字段,@,^后面的<,>,|决定了字段的长度,如果变量超出了定义的长度则将被截断。
  • <,>,|还分别表示左对齐、右对齐、居中对齐。
  • ^ 用于多行文本块的填充。

值域格式

  • @<<<:左对齐
  • @>>>:右对齐
  • @|||:居中对齐
  • @###.##:固定精度的数字
  • @*:多行文本

时间日期

获取函数的方法:

  • time():返回从 1970 年 1 月 1 日起累计的秒数
  • localtime():返回本地时区的时间
  • gmtime():返回格林威治时间

time

1
$epoc = time();  # 1585702883

localtime

sec, # 秒, 0 到 61
min, # 分钟, 0 到 59
hour, # 小时, 0 到 24
mday, # 天, 1 到 31
mon, # 月, 0 到 11
year, # 年,从 1900 开始
wday, # 星期几,0-6,0 表示周日
yday, # 一年中的第几天,0-364,365
isdst # 如果夏令时有效,则为真

1
2
3
4
5
6
7
8
9
10
# 分项返回
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();

# 输入时间戳返回时间
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($epoc);

# 返回时间字符串
$datestring = localtime();
print "$datestring\n";
# Sun Jun 12 11:27:31 2016

gmtime

1
2
# gmt 时间与中国时间差了8小时。
$gmt_datestring = gmtime();

格式化时间

1
printf("%02d:%02d:%02d", $hour, $min, $sec);
符号 描述 实例
%a 星期几的简称( Sun..Sat) * Thu
%A 星期几的全称( Sunday..Saturday) * Thursday
%b 月的简称(Jan..Dec) * Aug
%B 月的全称(January..December) * August
%c 日期和时间 _ Thu Aug 23 14:55:02 2001
%C 年份除于 100,并取整 (00-99) 20
%d 一个月的第几天 (01-31) 23
%D 日期, MM/DD/YY 相等于%m/%d/%y 08/23/01
%e 一个月的第几天,使用空格填充个位数 ( 1-31) 23
%F YYYY-MM-DD 的简写类似于 %Y-%m-%d 2001-08-23
%g 年份的最后两位数 (00-99) 01
%g 2001
%h 月的简称 (和%b 选项相同) Aug
%H 24 小时制 (00-23) 14
%I 12 小时制 (01-12) 02
%j 一年的第几天 (001-366) 235
%m 月 (01-12) 08
%M 分钟 (00-59) 55
%n 新行 (‘\n’)
%p 显示出 AM 或 PM PM
%r 时间(hh:mm:ss AM 或 PM),12 小时 * 02:55:02 pm
%R 24 小时 HH:MM 时间格式,相等于 %H:%M 14:55
%S 秒数 (00-61) 02
%t 水平制表符 (‘\t’)
%T 时间(24 小时制)(hh:mm:ss),相等于%H:%M:%S 14:55
%u ISO 8601 的星期几格式,星期一为 1 (1-7) 4
%U 一年中的第几周,星期天为第一天(00-53) 33
%V ISO 8601 第几周 (00-53) 34
%w 一个星期的第几天(0 代表星期天) (0-6) 4
%W 一年的第几个星期,星期一为第一天 (00-53) 34
%x 显示日期的格式(mm/dd/yy) 08/23/01
%X 显示时间格式 14:55:02
%y 年,两位数 (00-99) 01
%Y 2001
%z ISO 8601 与 UTC 的时区偏移(1 minute=1, 1 hour=100) +100
%Z 当前时区的名称,如”中国标准时间” CDT
%% % 符号 %
1
2
3
4
5
6
#!/usr/bin/perl
use POSIX qw(strftime);

$datestring = strftime "%Y-%m-%d %H:%M:%S", localtime;
printf("时间日期 - $datestring\n");
# 时间日期 - 2016-06-12 12:15:13

错误处理

通过 if..else / unless / 三元运算符来监控

错误信息存储在 $!中。

1
2
3
4
5
6
7
8
9
10
11
if (open(DATA, $file)){

}else{
die "ERROR: 无法打开文件 - $!";
}

unless (open(DATA, $file)){
die "ERROR: 无法打开文件- $!";
}

print(exists($hash{value}) ? '存在': '不存在');

warn / die 函数

warn 函数用于触发一个警告信息,不会有其他操作,输出到 STDERR(标准输出文件),通常用于给用户提示。
die 函数与 warn 类似,但是会在输出错误日志后执行退出。

1
2
warn "无法打开文件";
die "无法打开文件";

Carp 模块

标准 Carp 模块提供了 warn() 和 die() 函数的替代方法,同时会提示模块名称和行号。

  • carp 函数
    等价于 warn 函数。
  • cluck 函数
    等价于 warn 函数,提供了从产生错误处的栈回溯追踪。
  • croak 函数
    等价于 die 函数,结束程序。
  • confess 函数
    等价于 die 函数,提供了从产生错误处的栈回溯追踪。

特殊变量

@INC:指向程序所在位置的目录路径。

面向对象

包和模块

包 package

Perl 中每个包有一个单独的符号表,定义语法为:

1
package mypack;
  • 此语句定义一个名为 mypack 的包,在此后定义的所有变量和子程序的名字都存贮在该包关联的符号表中,直到遇到另一个 package 语句为止。
  • 从一个包中访问另外一个包的变量,可通过” 包名 + 双冒号( :: ) + 变量名 “ 的方式指定。

BEGIN / END

Perl 语言提供了两个关键字:BEGIN,END。它们可以分别包含一组脚本,用于程序体运行前或者运行后的执行。

1
2
3
4
5
6
BEGIN{
print "begin 语句";
}
END{
print "end 语句";
}

模块

模块的定义

模块是一个可以重复使用的包,模块的名字与包名相同,定义模块的文件后缀为 .pm
示例:

1
2
3
4
5
6
7
8
9
package Test;
sub bar {
print "hello $_[0]\n";
}

sub blat{
print "world $_[0]\n";
}
1;

末尾的 1;执行返回 TRUE,这是必须的,否则返回错误。

模块的调用

模块可以通过 require来调用,也可以用use来引用。二者的区别在于:

  1. require 引用后需要使用包名指定函数,而 use 不需要。
1
2
require Test;
Test.bar("a");
1
2
use Test;
bar("a");
  1. require 可以载入模块和 perl 程序(带.pl 后缀);且可以指定路径引入;而 use 只能引入当前文件路径下的模块。
  2. use 是编译时引入的,require 是运行时引入的。
  3. use 引入模块时,也引入了模块的子模块;而 require 不能引入子模块,需要重新声明。
  4. use 引用模块时,如果模块名称包含了::,则该双冒号将被座位路径分隔符,相当于’\’。
1
use MyDirectory::MyModule;

创建 Perl 模块

在创建好 .pm文件后,通过在命令行执行h2xs -AX -n ModuleName来编译模块,使其可以被调用。此时会在.pm文件创建一个同名目录,包括了一系列源码、测试脚本、日志、安装信息等文件,可以将该目录打包为 tar.gz。

安装 Perl 模块

对打包的 tar.gz 解压安装即可安装 perl 模块。

1
2
3
4
5
tar xvfz ModuleName.tar.gz
cd ModuleName
perl Makefile.PL
make
make install

POD 文档

POD(Plain Old Documentation) 文档的格式为:以 =head1 开始, =cut 结束, =head1 前与 =cut 后添加一空行。

1
2
3
=head1 Hello, World 实例
这是一个 Perl 的简单实例。
=cut

还可以使用 __END____DATA__ 将所在行之后的内容全部”注释”掉:

1
2
3
4
5
6
7
8
9
10
11
print "Hello, World\n";

while(<DATA>){
print $_;
}

__END__

=head1 Hello, World 实例
这是一个 Perl 的简单实例。
print "Hello, Runoob\n";

其他

文件操作

目录操作

正则表达式

进程管理

发送邮件

Socket 编程

数据库连接

CGI 编程

1
2