Perl
- 大量出于实用主义而设定的默认行为
- 使用了大量符号和缩写方式, 代码可以表示得很奇异
这种做法事实上降低了可读性, 但部分Perl用户认为这种写法更好. - 以正则表达式和文本处理能力为重心
- JavaScript明显大量借鉴了Perl的语言特征
- 虽然出发点是好的(例如减少无谓的键盘输入量),
但Perl的默认行为太多了, 对于Perl不熟悉的人很难理解Perl代码. - 和Python一样, 没有字面量的自动装箱机制.
这使得命令不能通过REPL环境被用户发现, 需要通过查手册得知, 也限制了编辑器的发挥. - 学习成本很高, 教材很差, 语言差异大.
#!usr/bin/perluse 5.010;say "Hello World!";
use 5.010;
是为了启用Perl 5.10的say函数.Perl的次版本号固定为三位数字, 因此必须输入5.010而不是5.10.
也可以用
#!/usr/bin/env perl
作为shebang.Perl里只有双精度浮点数.
1.25255.000255.07.25e45-6.5e24-12e-24-1.2E-2325561_298_040_283_768 # 用下划线增强可读性, 相同的语法已在JavaScript里引入0377 # 八进制0xff # 十六进制0b11111111 # 二进制0x1377_0B770x50_65_72_7C
2+35.1-2.43*1214/210.2/0.310/310%32**3
==
相等!=
不等<
小于>
大于<=
小于或等于>=
大于或等于
# 单引号仅可转义\和'两种字符.'fred''barney''''Dont\'t let an apostrophe end this string prematurely!''the last character is a backslash: \\''hellothere''\n' # 不是换行, 而是两个字符# 双引号可转义所有字符"barney""hello world\n" # 最后的是换行符"The last character of this string is a quote mark: \"""coke\tsprite""\x{2668}" # Unicode的代码点(code point)$alef = chr(0x05D0); # 直接用chr函数转换代码点$alpha = chr(hex('03B1'));ord('?')# chr的逆运算$meal = "brontosaurus steak";$barney = "fred ate a $meal"; # 标量变量内插, 如果标量变量没被赋值过, 就以空字符串代替$barney = 'fred ate a ' . $meal; # 等价写法$barney = "fred ate a ${meal}"; # 用于防止变量名出现歧义的写法@rocks = ('flintstone', 'slate', 'rubble');print "quartz @rocks limestone\n"; # 内插数组时, 会以空格间隔开每个元素的值print "$rocks[1]"; # 内插数组的元素, 下标部分可以是表达式
use utf8; # 开启UTF-8(请确保文件也以UTF-8编码保存)
"hello" . "world" # 字符串连接(与PHP一致)"fred" x 3 # 将字符串重复3次, 该运算符不满足交换律, 左值一定是被重复的字符串, 右值一定是重复的次数."barney" x (4+1)5 x 4.8 # 左值会自动转换为字符串, 右值会自动取整, 相当于 "5" x 4, 得"5555"
eq
相等ne
不等lt
小于gt
大于le
小于或等于ge
大于或等于
Perl没有专门的布尔值类型, 判定根据以下规则:
- 数据类型为数字时, 0为假, 其他数字为真.
- 数据类型为字符串时, 空字符串
''
和'0'
(在Perl里与数字0为同一个标量值)为假, 其他字符串为真. - 如果不是数字也不是字符串, 则先转换为数字或字符串再判断.
- 如果是引用, 则undef为假, 其他引用为真.
- 如果是哈希, 至少有一个键值对为真, 空哈希为假.
需要注意的是, Perl的文档并未规定Perl的布尔真值应该为哪个具体的值.
例如, 数字的真值并未规定为1, 尽管在实现时返回的是1.
例如, 数字的真值并未规定为1, 尽管在实现时返回的是1.
!
取反
Perl根据运算符对左值和右值分别进行自动转换.
例如
例如
"12" * "3"
的结果会是36, 因为乘法运算符会将操作数自动转换为数字.字符串转换为数字时, 非数字的部分会被忽略:
完全不包含数字的字符串会被转换为0.
字符串转换时不会关注"前置零"的存在, 因此
"12fred34" * "3"
的结果为36, 原因在于 12
之后的部分都被忽略.完全不包含数字的字符串会被转换为0.
字符串转换时不会关注"前置零"的存在, 因此
'0377'
会被转换为十进制的377.use strict; # 从Perl 5.12开始, 如果指定最低版本号, 则自动隐含此编译指令# JavaScript的严格模式似乎就是从中继承而来my $bamm_bamm = 3 # 严格模式下必须先用my声明新的变量, 然后才能使用
Perl的警告信息可以警告程序员存在一些自动类型转换,
程序员通常会希望避免这些自动转换的发生.
程序员通常会希望避免这些自动转换的发生.
#!/usr/bin/perluse warnings; # 开启内置警告信息(从Perl 5.6开始)
$ perl -w ./my_program # 从命令行开启内置警告信息
#!/usr/bin/perl -w # 用shebang开启内置警告信息
#!/usr/bin/perluse diagnostics; # 显示详细的诊断信息, 这会降低程序的启动速度, 并且需要消耗更多内容
$ perl -Mdiagnostics ./my_program
存储单个标量值的变量.
以魔符
在开启utf8编译指令时, 也可以用Unicode字符作为变量名.
以魔符
$(sigil)
开头, 以字母, 数字, 下划线构成标识符, 区分大小写.在开启utf8编译指令时, 也可以用Unicode字符作为变量名.
$fred = 17;$barney = 'hello';$barney .= ' ';$barney .= 'world';$fred = $fred + 3;$fred = $fred * 2;$fred += 5;$fred *= 3;$fred **= 3;
Perl里的数组与标量变量使用两个不同的命名空间,
因此一个变量名可以被重复用于数组和标量变量, 两者是独立运行的.
但出于可维护性的角度不鼓励这么做.
因此一个变量名可以被重复用于数组和标量变量, 两者是独立运行的.
但出于可维护性的角度不鼓励这么做.
$fred[0] = "yabba";$fred[1] = "dabba";$fred[2] = "doo";
任何数字表达式都可以用作数组下标, 如果不是整数, 则会自动舍去小数部分.
未使用的数组元素, 值为undef.
未使用的数组元素, 值为undef.
$end = $#rocks; # 数组的最后一个下标$number_of_rocks = $#rocks + 1; # 数组的大小$rocks[$#rocks] = 'hard rock';$rocks[-1] = 'hard rock'; # 等价写法, 负索引值
(1, 2, 3)(1, 2, 3,) # 末尾的逗号被省略("fred", 4.5)() # 空列表(1..100) # 从1到100的100个整数(1.7..5.7) # 从1到5的5个整数, 小数部分被舍去(5..1) # 空列表, 范围操作符仅能从左值到右值自增(0, 2..6, 10, 12) # (0, 2, 3, 4, 5, 6, 10, 12)($m..$n)(0..$#rocks)
Perl的一种列表直接量的简写方式, 可以用空白符替代引号.
qw(fred barney betty wilma dino)# 等价于 ('fred', 'barney', 'betty', 'wilma', 'dino')qw( fred barney betty wilma dino)# 自定义定界符qw/ fred barney betty wilma dino / qw# fred barney betty wilma dino #qw{ fred barney betty wilma dino }qw! yahoo\! google ask man ! # 将yahoo!作为一个元素, 转义被用作定界符的符号
($fred, $barney, $dino) = ('flintstone', 'rubble', undef);($fred, $barney) = ($barney, $fred); # 交换# 不对称情况($fred, $barney) = ('flintstone', 'rubble', 'slate'); # 多余的元素会被忽略($wilma, $dino) = ('flintstone'); # $dino的值为undef# 数组引用@rocks = ('bedrock', 'slate', 'lava');@tiny = ();@giant = 1..1e5;@stuff = (@giant, undef, @giant); # 列表中的@giant会自动展开, 如果数组是空列表, 则会被忽略.@copy = @quarry;
几乎就是JavaScript的数组方法.
pop(@array);pop @array;push(@array, 0);push(@array, 1..10);push @array, 1..10;shift(@array);shift @array;unshift(@array, 0);unshift(@array, 1..10);unshift @array, 1..10;@array = qw( pebbles dino fred barney bety );@removed = splice @array, 2; # 删掉fred及之后的元素@array = qw( pebbles dino fred barney bety );@removed = splice @array, 0, 2; # 删掉pebbles和dino@array = qw( pebbles dino fred barney bety );@removed = splice @array, 1, 2, qw(wilma); # 用('wilma')替换dino和fred@array = qw( pebbles dino fred barney bety );@removed = splice @array, 1, 0, qw(wilma); # 不删除元素, 只插入元素@result = reverse 6..10;@result = sort 97..102;while (my($index, $value) = each @rocks) { say "$index: $value";}
# 面对不同的上下文, Perl会采取不同的默认行为, 这种默认行为很大程度上由语言的作者Larry决定@backwards = reverse qw/ yabba dabba doo /; # doo, dabba, yabba$backwards = reverse qw/ yabba dabba doo /; # oodabbadabbay@wilma = undef # 这将得到(undef)而不是undef@betty = () # 清空数组@rocks = qw( talc quartz jade obsidian );print "How many rocks do you have?\n";print "I have ", @rocks, " rocks!\n"; # 本想输出数组个数, 却输出了数组print "I have ", scalar @rocks, " rocks!\n"; # 通过scalar操作符强制以标量上下文形式工作
$family_name{'fred'} = 'flintstone';$family_name{fred} = 'flintstone'; # 键名左右的引号可省略, 但要注意不被当作表达式%family_name # 访问整个哈希%some_hash = ( 'foo', 35, 'bar', 12.4); # 数组收缩为哈希@any_array = %some_hash; # 哈希展开(unwinding)为数组# 胖箭头表示法%last_name = ( 'fred' => 'flintstone', 'dino' => undef, 'barney' => 'rubble', 'betty' => 'rubble',);# 对于由字母, 数字, 下划线构成且不以数字开头的键, 键名左右的引号可以省略%last_name = ( fred => 'flintstone', dino => undef, barney => 'rubble', betty => 'rubble',);
my %hash = ('a' => 1, 'b' => 2, 'c' => 3);my @k = keys %hash;my @v = values %hash;my $count = keys %hash;while (($key, $value) = each %hash) { print "$key => $value\n";}foreach $key (sort keys %hash) { print "$key => $hash{$key}\n";}if (exists $book{'dino'}) {}delete $books{'dino'}
未赋值的变量值为undef, 类似于JavaScript的undefined.
当undef被视作数字时, 会把它当作0.
当undef被视作字符串时, 会把它当作空字符串.
当undef被视作数字时, 会把它当作0.
当undef被视作字符串时, 会把它当作空字符串.
使用defined函数判断一个值是否是undef:
$madonna = <STDIN>; # Perl将undef作为EOF使用.if (defined($madonna)) { print "The input was $madonna";} else { print "No input available!\n";}
$_ = "yabba dabba doo";if (/abba/) { print "It matched!\n";}# 使用Unicode属性, 这些属性可以在perluniprops文档中查询得到if (/\p{Space}/) { # 空白符(共有26个字符带有此属性)}if (/\p{Digit}/) { # 数字(共有411个不同的数字字符带有此属性)}# 从 Perl 5.6开始, 一些正则表达式里的简写形式代表的字符集比过去要大得多(因为支持了ASCII以外的字符)# 如需改变这一行为, 可以通过 Perl 4.14 的修饰符 =/a= (写在正则表达式末尾flag的位置)严格要求以ASCII语义展开:use 5.014;$_ = 'The HAL-9000 requires authorization to continue.';if (/HAL-[\d]+/a) {}# 绑定操作符(不使用$_变量的匹配方法)my $some_other = "I dream of betty rubble.";if ($some_other =~ /\brub/ { print "Aye, there's the rub.\n";}my $what = "larry"while (<>) { if (/\A($what)/) { # 内插 print "We saw $what in beginning of $_"; }}# 捕获变量(这些变量会存活到下一次成功匹配为止)$_ = "Hello there, neighbor";if (/\s(\[a-zA-Z]+),/) { print "the word was $1\n"; # 捕获到$1}# 具名捕获, 具名反向引用use 5.010;my $names = 'Fred Flintstone and Wilma Flintstone';if ($names =~ m/(?<last_name>\w+) and \w+ \g{last_name}/) { say "I saw $+{last_name}";}# 替换$_ = "green scaly dinosaur";s/(\w+) (\w+)/$2, $1/;$_ = "fred flintstone";if (s/fred/wilma/) {}$_ = "I saw Barney with Fred.";s/(fred|barney)/\U$1/gi; # 转大写s/(fred|barney)/\L$1/gi; # 转小写s/(\w+) with (\w+)/\U$2\E with $1/i; # I saw FRED with barney. \E用于界定大小写转换的范围# 小写形式的 \u, \l 只转换一个字符# 拆分字符串my @fields = split /separator/, $string;# 合成字符串my $result = join $glue, @pieces;# 列表上下文的模式匹配$_ = "Hello there, neighbor!";my($first, $second, $third) = /(\S+) (\S+), (\S+)/;print "$second is my $third\n";
/fred/
实为模式匹配操作符 m/fred/
的简写, 其关键在于斜杠这一定界符是可以被替换的.也就是说, 可以存在
m(fred)
, m<fred>
, m{fred}
等形式./s
修饰符可以让点号匹配任意字符, 包括原先不能匹配的换行符./x
修饰符允许我们在模式里加上空白符, 却不影响语义, 这使得正则表达式的可读性获得提升./-?[0-9]+\.?[0-9]*//-? [0-9]+ \.? [0-9]*/x/ -? # 一个可有可无的减号 [0-9]+ # 小数点前必须出现一个或多个数字 \.? # 一个可有可无的小数点 [0-9]* # 小数点后面的数字, 有没有都没关系/x
$line = <STDIN>;if ($line eq "\n") { print "That was just a blank line!\n";} elsif ($line eq "\t") { print "That was just a tab line!\n";} else { print "That line of input was: $line";}unless ($line eq "\n") { print "That line of input was: $line";} # 尽管语法上支持else, 但出于语义考虑不建议添加print "$n is a negative number.\n" if $n < 0;expression ? if_true_expr : if_false_exprgiven ($ARGV[0]) { when ('Fred') { say 'Name is Fred' } when (/fred/i) { say 'Name has fred in it' } when (/\AFred/) { say 'Name starts with Fred' } default { say "I don't see a Fred" }}given ($ARGV[0]) { # ~~ 智能匹配(由大量关于左右值类型的默认行为决定匹配方式) when ($_ ~~ 'Fred') { say 'Name is Fred' } when ($_ ~~ /\AFred/) { say 'Name starts with Fred' } when ($_ ~~ /fred/i) { say 'Name has fred init' } default { say "I don't see a Fred" }}
$count = 0;while ($count < 10) { $count += 2; print "count is now $count\n"; last if $condition1 # last相当于其他语言的break next if $condition2 # next相当于其他语言的continue}$count = 0;until ($count >= 10) { $count += 2; print "count is now $count\n";}LINE: while (<>) { # 带标签的循环 foreach (split) { last LINE if /__END__/; # 带标签的last }}
foreach $rock (qw/ bedrock slate lava /) { print "One rock is $rock.\n";}# 循环执行完毕后, 控制变量$rock的值会被自动恢复为循环执行前的值# 在其他编程语言里, 相同的行为主要是以作用域的形式存在foreach my $rock (qw/ bedrock slate lava /) { # 词法作用域的控制变量 print "One rock is $rock.\n";}foreach (1..10) { # 省略控制变量时, 则默认使用$_作为控制变量 print "I can count to $_!\n";}
除非函数调用时去掉括号会改变表达式的意义, 否则括号可以省略.
函数调用和列表表达式都使用括号作为标识符, 因此推荐在函数/子程序调用后加上括号, 以免被Perl错误理解.
函数调用和列表表达式都使用括号作为标识符, 因此推荐在函数/子程序调用后加上括号, 以免被Perl错误理解.
sub marine { $n += 1; # 所有子程序中的变量都是全局变量 print "Hello, sailor number $n!\n"; # 返回值}&marine; # 调用(calling)子程序sub marine { state $n = 0; # 在Perl 5.10引入的持久性私有变量(等同于其他语言的静态变量static) $n += 1; print "Hello, sailor number $n!\n";}sub sum_of_fred_and_barney { print "Hey, you called me"; $fred + $barney; # 最后一条语句的结果被视作返回值, 也可以用return手动返回(主要用于提前返回)}sub max { # 第一个参数为$_[0], 第二个参数为$_[1] if ($_[0] > $_[1]) { $_[0]; } else { $_[1]; }}$n = &max(10, 15);sub max { my($m, $n); # 用my操作符创造词法变量(私有变量) ($m, $n) = @_; # 获得子程序的参数, 可以和上一行一起缩写成my($m, $n) = @_; if ($m > $n) { $m; } else { $n; }}sub max { my($max_so_far) = shift @_; # 推出第一个参数, 如果参数列表是空的, 则会返回undef, 仍然符合预期行为 foreach (@_) { # 遍历剩余的参数列表 if ($_ > $max_so_far) { # 比较$max_so_far和当前的第一个参数 $max_so_far = $_; } } $max_so_far;}
- 在调用子程序之前编译器看到过子程序的定义(内置函数的工作原理)
- Perl判定该语法只能是一个子程序
my @cards = shuffle(@deck_of_cards); # 省略了shuffle前的&
如果子程序名与内置函数的名称相同, 省略&时调用的将是内置函数.
推荐在自定义的子程序调用前加上&, 用以区别内置函数.
推荐在自定义的子程序调用前加上&, 用以区别内置函数.
文件句柄建议用大写字母命名.
# 打开文件句柄用的描述符与Unix Shell一致open CONFIG, 'dino';open CONFIG, '<dino';open BEDROCK, '>fred';open LOG, '>>logfile';# Perl 5.6以后的三参数写法, 内插字符串时更安全:open CONFIG, '<', 'dino';open BEDROCK, '>', $file_name;open LOG, '>>', &logfile_name();open CONFIG, '<:encoding(UTF-8)', 'dino'; # 指定编码读open BEDROCK, '>:encoding(UTF-8)', $filename_name; # 指定编码写, 会确认编码是否正确open BEDROCK, '>:utf8', $file_name; # 简写, 不会确认编码是否正确, 不推荐open BEDROCK, '>:crlf', $file_name; # 以CR-LF换行写入open BEDROCK, '<:crlf', $file_name; # 以CR-LF换行读入my $success = open LOG, '>>', 'logfile';if (!$success) { # 操作失败}if (!open LOG, '>>', 'logfile') { die "Cannot create logfile: $!"; # 终止程序运行, $!是操作系统提供的错误信息 # warn "Cannot create logfile: $!"; # 输出警告}# Perl 5.10加入了autodie编译指令, 会在open失败时自动die.use autodie;close BEDROCK; # 关闭句柄, 此外Perl具有在重复打开同一个文件时自动关闭上次打开的句柄的默认行为print LOG "Captain's log, stardate 3.14159\n"; # 打印到文件句柄select BEDROCK; # 改变默认输出print "I hope Mr. Slate doesn't find out about this.\n";print "Wilma!\n";select STDOUT; # 设置回默认值$| = 1 # 将缓冲区设置为1(在输出后立即刷新缓冲区)
# 打印Perl能理解的所有字符编码$ perl -MEncode -le "print for Encode->encodings(':all')"
- STDIN
- STDOUT
- STDERR
- DATA
- ARGV
- ARGVOUT
use File::Basename;use File::Basename qw/ basename /; # 部分导入use File::Spec;my $new_name = File::Spec->catfile($dirname, $basename);