Perl的数据类型大致可以分为四类:Scalar(标量)、Array(数组)、Hash(哈希)以及Reference(引用)。
标量是Perl中最简单的一种数据类型,也是构成其他三个数据类型的基石。标量变量可以代表一个字符、字符串以及数字(整数或浮点数)。
在Perl内部,它总是以“双精度浮点数(double-precision floating-point)”的要求来保存数字并进行运算。
1.25
255.000
7.25e45
-6.5e24
Perl允许我们在整数直接量中插入下划线
0
2001
-4
61_298_040
八进制(octal)直接量以0开头,十六进制(hexadecimal)直接量以0x开头,二进制(binary)直接量以0b开头。十六进制的A到F(or a到f)代表十进制的10到15
0377 # 八进制的377,等于十进制的255
0xff # 十六进制的FF,等于十进制的255
0b11111111 # 二进制,等于十进制的255
0x50_65_72_7c #十六进制
在单引号内的字符串直接量除了单引号和反斜线字符外,单引号内所有字符都代表它们自己(包括换行符)。如需代表反斜杠字符本身,需要再反斜线字符前再加一个反斜线字符表示转义
'fred' # 总共4个字符:f,r,e,d
'hello\n' #hello后面接着反斜线和字母n
'\'\\' # 单引号紧接着反斜线(总共2个字符)
双引号中的反斜线可以转义许多控制字符,或是用八进制或十六进制写法来表示任何字符。
"hello world\n" # hello world,后面接着换行符
coke\tsprite # coke、制表符(tab)和sprite
\x{2668} # Unicode中名为HOT SPRINGS的字符的代码点(code point)
双引号内字符串的反斜线转义
组合 | 意义 |
---|---|
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\f | 换页符 |
\b | 退格 |
\a | 系统响铃 |
\e | ESC(ASCII编码的转义字符) |
\007 | 八进制表示的ASCII值(此例中007表示系统响铃) |
\x7f | 十六进制表示ASCII值(此例中7f表示删除键的控制代码) |
\x{2744} | 十六进制表示的Unicode代码点(这里的U+2744表示雪花形状的图形字符) |
\cC | 控制符,也就是Control键的代码(此例表示同时按下Ctrl键和C键的返回码) |
\ | 反斜线 |
\ | 双引号 |
\l | 将下个字母转为小写的 |
\L | 将它后面的所有字母都转为小写的,知道\E为止 |
\u | 将下个字母转为大写的 |
\U | 将它后面的所有字母都转为大写的,知道\E为止 |
\Q | 相当于把它到\E之间的非单词(non word)字符加上反斜线转义 |
\E | 结束\L、\U和\Q开始的作用范围 |
字符串连接(.)
"hello" . "world" # 等同于"helloworld"
'hello world' . "\n" #等同于"hello world\n"
字符/字符串重复(x)
"fred" x 3 # 得"fredfredfred"
5 x 4.8 #本质上就是 5 x 4, 得 "5555"
Perl会根据需要,自动在数字和字符串之间进行类型转换。是否发生转换由操作符决定,如果操作符(如+)需要的是数字,Perl就回将操作数视为数字;在操作符(比如.)需要字符串时,Perl便会将操作数视为字符串。
# “前置零”的技巧只对直接量有效
0377 # 十进制数字255的八进制写法
'0377' # 会转换成十进制数字377
"Z" . 5 * 7 # 等同于"Z".35,得"Z35"
列表(list)指的是标量的有序集合,数组(array)是存储列表的变量。更精确的说,列表指的是数据,而数组指的是变量。
列表是包含在括号里的一序列的值,可以为任何数值,也可为空
(1,2,3) # 包含1、2、3三个数字的列表
( ) # 空列表——0个元素
(1..100) # 100个整数构成的列表
("fred","barney","betty","wilma","dino")
qw简写可以省去键入无谓引号的麻烦,定界符(delimiter)可以hi任何标点符号,如:()、!、/、#、{}、[]、…
qw( fred barney betty wilma dino )
qw! fred barney betty wilma dino !
qw/ fred barney betty wilma dino /
qw# fred barney betty wilma dino #
列表的赋值:
($fred, ¥barney, $dino)=("A", "B", "C")
列表存贮于Perl数组变量中,与简单变量不同,Perl数组变量以字符”@”打头,如
@array=(1,2,3);
注:
(1)Perl数组变量创建时初始值为空列表:()。
(2)Perl中数组变量和标量标量处于不同的命名空间,所以同一个变量名可以同时用作数组变量和标量变量而不用担心命名冲突。所以同一个名字可以同时用于Perl数组变量和简单变量
数组中元素可以通过下标操作符[]表示,第一个元素的下标为0,最后一个元素的下标为-1。试图访问不存在的数组元素将得到undef,如果给超出数组大小之外的元素赋值,则Perl数组将自动增长,原来没有的元素的值为undef。如下:
my @arr = (1, 2, 3);
my $var = $arr[5]; # $var will get undef
$arr[5] = 8; # @arr changes to (1,2,3,undef,undef,8)
最后一个元素的索引值是$#rocks
哈希是一种数据结构,它和数组的区别在于:不像数组以下标来检索元素,哈希使用键来检索元素。哈希结构中每一个元素都有一个键和一个值,键必须唯一。
my %hash = (1 => "fred", 2.5 => 1, "fred" => 2.5);
符号”=>” 在Perl中被戏称为胖箭头,它很好的展示了哈希结构中键与值的映射关系。
在Perl中胖箭头”=>”与逗号”,”是完全等价的,因此上面右边的散列中的胖箭头完全可以用逗号代替。使用胖箭头的唯一理由就是它清晰的表达了各个键与值映射关系。
访问哈希元素:$hash{$some_key}
访问整个哈希:%family_name
哈希赋值:my %new_hash = %old_hash
建立一个反序的哈希:my %inverse_hash = reverse %any_hash
应该说引用使得Perl中数组和哈希变的更加灵活而强大。引用表达的是一种“指向”的关系,因此它类似于C语言中的指针。比如你创建一个引用让它指向一个数组:
my $ref1 = \@arr;
my $ref2->[0] = "first";
$ref2->[2] = 3;
这两种方法都会创建一个引用,第一个方法要求数组@arr已经被声明。第二种方法更像是创建了一个类似于数组的怪物,之所以说它是怪物是因为你如果需要访问数组的某个元素,你只能使用箭头符号”->”来完成下标的指定,类似于:
my $var = $ref->[num];
这里num显然是一个整数值。一般来说,引用的创建多用来指向到一个数组或者一个哈希。创建一个引用指向一个标量似乎没有太大的必要。
有了引用,我们就可以使用标量、数组以及哈希来任意地构造我们的数据结构,比如:
my $arr_double->[0] = \(1, 2, 3);
$arr_double->[1] = \(4, 5, 6);
$arr_double->[2] = \(7, 8, 9);
my $hash_double->{'line1'}->{'row1'} = "first";
$hash_double->{'line1'}->{'row2'} = "second";
Perl下的运算符来基本上借鉴于两种语言:C与Pascal。全部的运算符及其结合性如下:
表2-1 Perl中操作符
操作符 | 结合性 | 完成的操作 |
---|---|---|
() | 左 | 改变运算的优先级;列表操作符 |
-> | 左 | 引用操作符 |
++ – | 左 | ++自增,–自减 |
** | 右 | 乘幂 |
\ ! ~ + - | 右 | 都是单目操作符,\取地址,!逻辑非,~按位非,+正号,-负号 |
=~ !~ | 左 | =~匹配绑定操作符,!~不匹配绑定操作符(这东西太诡异了) |
* / % x | 左 | *乘,/除,%求余,x字符串重复操作符 |
<< >> | 左 | 移位操作符 |
-X -r rand shift | 左 | 具名的单目操作符,-X与-r都是文件测试操作符,rand得到一个随机值 |
< <= > >= lt le gt ge | 左 | “不等”关系运算符,前四个比较数值,后四个比较字符串 |
== != <=> eq ne cmp | 左 | “相等”关系操作符,前三个比较数值,后三个比较字符串 |
& | 左 | 按位与 |
^ | 左 | 按位或,^按位异或 |
&& | 左 | 逻辑与 |
左 | ||
.. … | 左 | ..范围操作符, |
?: | 右 | Perl中最诡异的操作符:三目(条件)操作符 |
= += x= .= | 右 | 赋值以及增量赋值操作符 |
, => | 左 | 逗号操作符;列表操作符(右结合性) |
not | 右 | 逻辑非 |
and | 左 | 逻辑与 |
or xor | 左 | or逻辑或,xor逻辑异或 |
如你所见,Perl中操作符很多很多,而且有很多冗余的,比如逻辑操作符有两套,and/not/or/xor系之所以存在,是因为它们的优先级很低,这 样使用它们有时你可以省敲一些括号。全部记住这些操作符及其优先级和更加让人头疼的结合性显然是不现实的,并且也有悖于Perl程序懒惰的作风,所以合理 的使用圆括号是值得推荐的,也很有必要。
上下文是一种很重要的概念,尽管我们未必留意到它,但是我们却无时无刻不在使用它。比如,某次考试后,你问别人“嘿,第21题怎么回事?”,你的同学应该可以立刻直到你在问什么。但如果你把这句话同样的说给刚刚睡醒爷爷听,他一定会感到莫名其妙。这就是上下文的概念。
在Perl中,上下文就是概念就是:同一个表达式,出现在不同的地方会有不同的结果和意义。
我们直到Perl中最最常见的就是标量和列表了,这形成了Perl中两种最常见上下文环境:标量上下文和列表上下文。标量上下文中的表达式被期望返回一个标量值,而列表上下文中的表达式则被期望返回一个列表。
这个情况没有统一的规定,但是,大多数情况下会返回列表元素的个数,这基本上就是你期望的。尽管如此,我还是打算详细说明一些比较“特殊”的。
某些表达式不会在标量上下文中返回任何值。比如,sort在标量上下文中返回的是undef。有人或许会认为应该返回列表元素的个数,可是谁会通过对列表排序来获得列表的个数呢?
某些表达式在标量上下文中会返回列表所有元素连接而成的串。比如reverse,在标量上下文中,它会返回逆序后的字符串(先将列表中所有元素串连在一起,在对结果进行反序处理)。
@letters = reverse qw/A B C D E/; # 会得到E,D,C,B,A
$letters = reverse qw/A B C D E/; # 会得到EDCBA
另外需要强调的是,对于print操作符,其后是一个列表上下文。
这种情况十分简单:如果表达式求值结果为标量值,则自动产生一个仅含此标量值的列表
@fred = 6 * 7; # 得到仅有单个元素的列表(42)
@barney = "hello" . ' ' . "world";
@wilma = undef # 结果会得到一个列表,而且仅有的一个元素为未定义的(undef)
@betty = ( ); # 正确的清空数组的方法
因为undef是标量值,所以将undef赋值给数组并不会清空该数组。要清空的话,直接赋予一个空列表即可。
在Perl想要列表上下文的地方你想要强制引入标量上下文,可以使用为函数scalar。它不是真正的函数,只是告诉Perl这里要切换到标量上下文。
@rocks = qw( talc quartz jade obsidian );
print "How many rcks do you have?\n";
print "I have ", @rocks, "rocks!\n"; #wrong,这里会输出各种石头的名称
print "I have ", scalar @rocks, "rocks!\n"; # right,打印出来的是石头种数
Perl中没有专用的布尔类型的值,关系运算的结果也会是一个标量值,Perl中任何标量值都可以作为控制结构的判断条件。Perl中判定一个标量为真还是假的规则很简单:
(1)如果值为数值,0为假,所有其它数值为真;
(2)如果值为字符串,空字符串(”“或”)为假,所有其它字符串为真;
(3)如果不是数值也不适字符串,那么就先转化成数值或字符串再进行判断,所以undef为假,所有的引用都为真;
需要注意的一点就是:字符串’0’或”0”跟数字0是同一个标量值,所以也为假,这是非空字符串为假的唯一的例外。
对于列表,现将它转化成标量值在进行判断(也就是说控制结构的判断条件是一个标量上下文)。
if (condition1){
something;
} elsif (condition2) {
someting;
} else {
something;
}
注意,Perl中控制结构的花括号不是可有可无的,而是必须存在的。Perl中暂时没有switch结构,在Perl6中或许会有这个结构。其它分支选择结构还有unless结构,只是比较少用。
为了进一步简化代码书写,表达式后面可以接一个用于控制它行为的修饰符。比如
print "$n is a negative number.\n" if $n < 0;
unless (condition) {
something;
} else {
something;
}
条件操作符就像将一个if-then-else控制结构。
expression ? if_true_expr : if_false_expr
while (condition){
something;
}
裸块就像一个while或foreach循环,只是它从不循环,它仅仅执行循环体一次,然后结束。所以裸块其实并非循环。裸块的结构如下:
{
body;
body;
body;
}
until (condition) {
something;
}
foreach $var (@array) {
something;
}
for (initialization;condition;increment) {
something;
}
last操作符能立即中止循环的执行,就像C这类语言中的”break”操作符一样。
# 打印输入中所有提到 fred 的行,直到碰到 _END_ 记号为止
while (<STDIN>) {
if(/_END_/) {
# 碰到这个记号说明再也没有其他输入了
last;
} elsif (/fred/) {
print;
}
}
有时候我们并不需要立刻退出循环,但需要立刻结束当前这次循环迭代。next操作符的用处就是它会跳到当前循环块的底端。在next之后,程序将会继续执行循环的下一次迭代,这和C这类语言中的”continue”操作符功能相似
# 分析输入文件中的单词
while {<>} {
foreach (spilt) {
$total++;
next if /\W/;
$valid++;
$count{$_}++;
## 上面的next语句如果运行,会跳到这里 ##
}
}
print "total things = $total, valid words = $valid\n";
foreach $word (sort keys %count) {
print "$word was seen $count{$word} times.\n";
}
redo能将控制返回到当前循环块的顶端,而不经过任何条件测试,也不会进入下一次循环迭代。
# 打字测验
my @words = qw{ fred barney pebbles dino wilma betty };
my $errors = 0;
foreach (@words) {
## redo 指令会跳到这里##
print "Type the word '$_':";
chomp(my $try = );
if ($try ne $_) {
print "Sorry - That's not right.\n\n";
$errors++;
redo; #跳到循环顶端
}
}
print "You've completed the test, with $errors errors.\n";
next和redo两者之间最大的区别在于,next会正常继续下一次迭代,而redo会重新执行这次迭代。
pop和push操作符是对数组的“尾端”进行处理
pop操作符:取出数组中最后一个元素并将其作为返回值返回,常用于删除数组中的最后一个元素
@array = 5..9; #原数组array是(5,6,7,8,9)
$fred = pop(@array); #$fred 变成9, @array现在是(5,6,7,8)
$barney = pop @array; #$barney 变成8,@array现在是(5,6,7)
注:如果数组是空的,pop什么也不做,直接返回undef
push操作符:添加一个元素(或一串元素)到数组尾端
@array = (5,6);
push (@array, 0); # @array现在是(5,6,0)
push @array, 8; # @array现在是(5,6,0,8)
push @array, 1..10; # @array得到了10个新元素
@others = qw / 9 0 2 1 0/;
push @array, @others; # @array又得到了5个新元素
shift和unshift操作符是对数组的“开头”进行相应的处理
@array = qw/ dino fred barney /;
$m = shift(@array); # $m变成"dino",@array现在是("fred","barney")
$n = shift @array; # $n变成"fred", @array现在是("barney")
shift @array; # 现在@array变空了
$o = shift @array; # $so变成undef,@aaray还是空的
unshift (@array, 5); # @array现在仅包含一个元素的列表(5)
unshift (@array, 4); # @array现在是(4, 5)
@others = 1..3;
unshift @array, @others; # @array现在是(1,2,3,4,5)
splice操作符是对数组“中间”进行相应的处理
splice可接受4个参数:
splice x1, x2, x3, x4
- x1:要操作的目标数组
- x2:要操作的一组元素开始的位置
- x3:指定要操作的元素长度
- x4:要替换的列表
示例1:
@array = qw( A B C D E );
@removed = splice @array, 2; #在原来的数组中删掉C及其后的元素
#@removed变成qw(C D E)
#原先的@array变成 qw(A B)
示例2:
@array = qw( A B C D E );
@removed = splice @array, 1, 2, qw(F); #删除B和C
#@removed变成qw(B C)
#@array变成qw(A F D E)
示例3:
@array = qw( A B C D E );
@removed = splice @array, 1, 0,qw(F); #什么元素都不删
#@removed变成qw()
#@array变成qw(A F B C D E)
reverse操作符会读取列表(或数组)的值,并按相反的次序返回该列表
示例:
@fred = 6..10;
@barney = reverse(@fred); #得10,9,8,7,6
@wilma = reverse 6..10; #同上
@fred = reverse @fred; #将逆序后的结果放回原来那个数组
注:reverse会返回次序相反的列表,但它不会修改传进来的参数。假如返回值无处可去,那么操作也是无意义的。
reverse @fred #错误——这不会修改@fred的值
sort操作符会读取列表(或数组)的值,根据内部的字符编码顺序对它们进行排序。对字符串而言,就是字符在计算机内部表示的代码点。
示例:
@rocks = qw/ bedrock slate rubble granite /;
@sorted = srot(@rocks); #得bedrock,granite,rubble,slate
注:与reverse操作符一样,你必须将排序后的结果存回数组
对数组调用each,会返回数组中下一个元素所对应的两个值——该元素的索引以及该元素的值。
use 5.012
my @rocks = qw/ bedrock slate rubble granite /;
while( my( $index,$value ) = each @rocks){
say "$index:$value";
}
如果不用each来实现,可以根据索引从小到大依次遍历,然后借助索引值取得元素的值
@rocks = qw/ bedrock slate rubble granite /;
foreach $index ( 0..$#rocks ){
print "$index:$rocks[$index]\n";
}
keys函数会返回此hash的所有keys,values函数将返回所有的values。如果hash中没有元素,则此函数将返回空列表。
my %hash = ("a"=>1, "b"=>2, "c"=>3);
print my @k = keys %hash;
print my @v = values %hash;
如果想迭代hash的每一个元素,一种通常的方法是使用each函数,它将返回key/value对应的2个元素列表。
当对同一个hash 函数进行一次迭代时,将返回下一个key/value对,直到所有的元素均被访问。如果没有更多的key/value对,则each函数将返回空表。
my %hash = ("a"=>1, "b"=>2, "c"=>3);
while(($key, $value) = each %hash)
{
print "$key => $value\n";
}
当然,each返回的key/vlaue对,顺序是混乱的(它其顺序和keys和values函数返回的顺序相同)。如果想将其按序排放,可以对它们排序(使用sort)。
my %hash = ("a"=>1, "b"=>2, "c"=>3, "d"=>4);
foreach $key (sort keys %hash)
{
$value =$hash{$key};
print "$key => $value\n";
}
要查看hash中是否存在某个key,可以使用exists函数,如果hash中存在此key,则返回true,与是否有对应的value无关。
my %hash = ("a"=>1, "b"=>2, "c"=>3, "d"=>4);
if(exists $hash{'a'})
{
print "true";
}
delete函数将某个给定的key(包括其对应的value)从hash中删除。如果不存在这个key,则什么也不做,不会有警告或者错误信息。
my %hash = ("a"=>1, "b"=>2, "c"=>3, "d"=>4);
delete $hash{'a'};
foreach $key (sort keys %hash)
{
$value =$hash{$key};
print "$key => $value\n";
}
函数以关键词sub申明,结构如下:
sub function_name {
something;
}
调用函数的方法如下:
&function_name(parameter_list);
对于函数,需要注意的是在调用时,函数的参数是一个列表上下文,所以才叫做参数列表嘛。传给函数的参数被放在函数的下划线变量@_中。Perl中函数的返回值可以通过return语句显示返回,如果没有return语句,则返回最后一个条语句的执行结果。
chomp操作符只能作用于单个变量,且该变量的内容必须为字符串,如果该字符串末尾是换行符,chomp()的任务就是去掉它。
chomp($text = );
split是使用正则表达式的操作符,它会根据给定的模式拆分字符串。对于使用制表符、冒号、空白或任意符号分隔不同字段数据的字符串来说,用这个操作符分解提取字段相当方便。
my @fields = split /separator/, $string;
例:
my @fields = split /:/, "ab:cde:f"; # 得到("ab", "cde", "f")
joint函数不会使用模式,它的功能与split恰好相反,join会把几个子字符串结合成一个字符串。
my $string = join $glue, @pieces;
join的第一个参数可以是任意字符串,弃于参数则是一串片段。
my @x = join ":", 4,6,8,10; # $x 为 "4:6:8:10"
index的作用是找出子字符串在主字符串中的相对位置
$where = index($Main_String, $Sub_String, $intial_position);
Perl会在$Main_String字符串 中寻找$Sub_String字符串首次出现的地方,并返回一个整数表示第一个字符的匹配位置,返回的字符位置从0算起。即:如果子字符串时在字符串最开始的位置找到,那么index会返回0。第三个参数 $intial_position 可以指定开始搜索的地方
substr函数只处理较长字符串中的一小部分内容
my $part = substr($string, $intial_position, $length);
它的三个参数为:一个原始字符串、一个从零起算的起始位置,以及子字符串长度。
print函数作用:接受标量值作为参数,然后不经修饰的将它传送到标准输出。
print "hello, world! \n";
printf的参数包括“格式字符串”及“要输出的数据列表”。
printf "Hello, %s; Your password expires in %d days!\n", $user, $days_to_die;
格式 | 作用 |
---|---|
%g | 输出数字,它将根据需要自动选用浮点数,整数或者指数 |
%d | 十进制整数,它会舍去小数点之后的数字。非四舍五入 |
%x | 十六进制 |
%o | 八进制 根据需要截尾 |
%s | 字符串格式,可以设定字段宽度。宽度字段如果是正数代表向右对齐,负数是相左对齐如%10s, %-15s |
%f | 针对数字的,转换格式(浮点数),会按需求四舍五入。如%12.3f (表示输出共12个字符,包括小数点,并且小数点后面只有3位数) |
%% | 输出百分号,不会使用后面列表中的元素 |
例子:
printf "%g %g %g\n", 5/2, 51/17, 51 ** 17; #2.5 3 1.0683e+29
printf "in %d days!\n", 17.85; #in 17 days!
printf "%6d\n",42 #从输出结果看起来像````42(符号`代表空格)
printf "%2d\n", 2e3 + 1.95; #2001
printf "%10s\n", "wilma"; #看起来像`````wilma
printf "%-15s\n", "flintstone"; #看起来像flintstone`````
printf "12f\n", 6*7 + 2/3; # ```42.66667
printf "12.3f\n", 6*7 + 2/3; # ``````42.667
printf "12.0f\n", 6*7 + 2/3; # ``````````43
sprintf函数与printf有相同的参数(可选的文件句柄参数除外),但它返回的是所请求的字符串,而不会直接打印出来。你可以将格式化后的字符串存在变量里以便稍后使用。此外,你也可以对结果进行额外的处理,而单靠printf是做不到这些的
sprintf FORMAT, LIST
FORMAT 包含一个带有嵌入的域指示符的文本,LIST 里的元素就是逐一替换到这些域中去的。sprintf 的格式:
域 | 含义 |
---|---|
%% | 一个百分号 |
%c | 一个带有给定数字的字符 |
%s | 一个字串 |
%d | 一个有符号整数,十进制 |
%u | 一个无符号整数,十进制 |
%o | 一个无符号整数,八进制 |
%x | 一个无符号整数,十六进制 |
%e | 一个浮点数,科学记数法表示 |
%f | 一个浮点数,用固定的小数点表示 |
%g | 一个浮点数,以 %e 或 %f 表示 |
另外,Perl 允许下列广泛支持的转换:
域 | 含义 |
---|---|
%x | 类似 %x,但使用大写字符 |
%E | 类似 %e,但使用大写的“E” |
%G | 类似 %g,但是带一个大写的“E”(如果正确的话) |
%b | 一个无符号整数,二进制 |
%p | 一个指针(输出十六进制的 Perl 值的地址) |
最后,为了向下兼容(我们的意思就是“向下”),Perl 允许下列不必要的但广泛支持的转换:
域 | 含义 |
---|---|
%i | %d 的同义词 |
%D | %ld 的同义词 |
%U | %lu 的同义词 |
%O | %lo 的同义词 |
%F | %f 的同义词 |
注:这些函数的详细介绍都可以通过perldoc -f functionname
查到
参考:
1、http://www.cnblogs.com/hiflex/archive/2012/08/03/2621353.html
2、《Perl语言入门》
3、http://www.jb51.net/article/30473.htm
4、http://www.perlcn.com/perlbc/perljj/1133.html