perltrap - Perl 陷阱
最大的陷阱是忘记使用 use warnings
或 -w 开关;请参阅 warnings 和 perlrun 中的 "-w"。第二个最大的陷阱是不能让你的整个程序在 use strict
下运行。第三个最大的陷阱是没读这个版本的 Perl 的变更列表;请参阅 perldelta.
习惯使用 awk 的用户应该特别注意以下几点
Perl 程序只执行一次,而不是对每行输入都执行一次。你可以使用 -n
或 -p
进行隐式循环。
English 模块,通过
use English;
加载,允许你使用名称(如 $RS)来引用特殊变量(如 $/
),就好像它们在 awk 中一样;有关详细信息,请参阅 perlvar。
在 Perl 中,所有简单语句后面都需要分号(除了块的末尾)。换行符不是语句分隔符。
if
和 while
需要使用花括号。
Perl 中的变量以 "$"、"@" 或 "%" 开头。
数组从 0 开始索引。类似地,`substr()` 和 `index()` 中的字符串位置也是从 0 开始。
您需要决定您的数组是使用数字索引还是字符串索引。
哈希值不会仅仅因为引用而存在。
您需要决定是使用字符串比较还是数字比较。
读取输入行不会自动将其拆分为数组。您需要自己将它拆分为数组。并且 `split()` 操作符的参数与 **awk** 的不同。
当前输入行通常位于 `$_` 中,而不是 `$0` 中。它通常不包含换行符。(`$0` 是执行的程序的名称。) 请参见 perlvar。
`$<数字>` 不指代字段,而是指代最后匹配模式匹配的子字符串。
`print()` 语句不会添加字段和记录分隔符,除非您设置了 `$,` 和 `$\`。如果您使用的是 English 模块,则可以设置 `$OFS` 和 `$ORS`。
您必须在打印到文件之前打开文件。
范围操作符是 "..",而不是逗号。逗号操作符的作用与 C 中相同。
匹配操作符是 "=~", 而不是 "~"。("~" 是按位取反操作符,与 C 中相同。)
指数运算符是 "**",而不是 "^"。"^" 是按位异或操作符,与 C 中相同。(您知道,人们可能会觉得 **awk** 与 C 基本不兼容。)
连接操作符是 ".",而不是空字符串。(使用空字符串将使 ` /pat/ /pat/ ` 不可解析,因为第三个斜杠将被解释为除法运算符 - 标记器实际上对于像 "/", "?", 和 ">" 这样的运算符略微依赖上下文。实际上,"." 本身可以是数字的开头。)
`next`、`exit` 和 `continue` 关键字的工作方式不同。
以下变量的工作方式不同
Awk Perl
ARGC scalar @ARGV (compare with $#ARGV)
ARGV[0] $0
FILENAME $ARGV
FNR $. - something
FS (whatever you like)
NF $#Fld, or some such
NR $.
OFMT $#
OFS $,
ORS $\
RLENGTH length($&)
RS $/
RSTART length($`)
SUBSEP $;
您不能将 `$RS` 设置为模式,只能设置为字符串。
如有疑问,请将 **awk** 结构通过 **a2p** 运行,看看它会返回什么。
C 和 C++ 程序员应该注意以下事项
if
和 while
语句需要使用花括号。
必须使用 elsif
而不是 else if
。
C 语言中的 break
和 continue
关键字在 Perl 中分别变为 last
和 next
。与 C 语言不同,它们在 do { } while
结构中不起作用。请参阅 "perlsyn 中的循环控制"。
switch 语句在 Perl 中被称为 given
/when
,并且仅在 Perl 5.10 或更高版本中可用。请参阅 "perlsyn 中的 Switch 语句"。
Perl 中的变量以 "$"、"@" 或 "%" 开头。
注释以 "#" 开头,而不是 "/*" 或 "//”。Perl 可能将 C/C++ 注释解释为除法运算符、未终止的正则表达式或定义或运算符。
你不能获取任何东西的地址,尽管 Perl 中有一个类似的运算符,即反斜杠,它创建了一个引用。
ARGV
必须大写。$ARGV[0]
等同于 C 语言中的 argv[1]
,而 argv[0]
则存储在 $0
中。
系统调用(如 link()、unlink()、rename() 等)在成功时返回非零值,而不是 0。(但是,system() 在成功时返回零。)
信号处理程序处理信号名称,而不是信号编号。使用 kill -l
在你的系统上查找它们的名称。
谨慎的 JavaScript 程序员应该注意以下几点
在 Perl 中,二进制 +
始终是加法。$string1 + $string2
将两个字符串转换为数字,然后将它们相加。要连接两个字符串,请使用 .
运算符。
+
一元运算符在 Perl 中没有任何作用。它存在是为了避免语法歧义。
与 for...in
不同,Perl 的 for
(也称为 foreach
)不允许左侧为任意表达式。它必须是一个变量。
for my $variable (keys %hash) {
...
}
此外,不要忘记其中的 keys
,因为 foreach my $kv (%hash) {}
会遍历键和值,通常没有用($kv 将是一个键,然后是一个值,依此类推)。
要遍历数组的索引,请使用 foreach my $i (0 .. $#array) {}
。foreach my $v (@array) {}
会遍历值。
Perl 要求在 if
、while
、foreach
等之后使用花括号。
在 Perl 中,else if
被拼写为 elsif
。
? :
的优先级高于赋值。在 JavaScript 中,你可以写
condition ? do_something() : variable = 3
并且只有在条件为假时才会对变量进行赋值。在 Perl 中,你需要使用括号
$condition ? do_something() : ($variable = 3);
或者直接使用 if
。
Perl 要求使用分号来分隔语句。
用 my
声明的变量只影响声明后的代码。你不能写 $x = 1; my $x;
并期望第一个赋值会影响同一个变量。相反,它会将值赋给之前在外部作用域中声明的 $x
,或者赋给一个全局变量。
还要注意,变量在下一个语句之前是不可见的。这意味着在 my $x = 1 + $x
中,第二个 $x 指的是之前声明的变量。
my
变量的作用域是当前块,而不是当前函数。如果你写 {my $x;} $x;
,第二个 $x
不会引用块内声明的变量。
对象的成员不能作为变量访问。最接近 Perl 中 with(object) { method() }
的等效语句是 for
,它可以将 $_
作为对象的别名。
for ($object) {
$_->method;
}
调用方法的对象或类作为方法参数之一传递,而不是作为单独的 this
值。
经验丰富的 sed 程序员应该注意以下几点
Perl 程序只执行一次,而不是对每行输入都执行一次。你可以使用 -n
或 -p
进行隐式循环。
替换中的反向引用使用 "$" 而不是 "\"。
模式匹配元字符 "(", ")", 和 "|" 前面没有反斜杠。
范围运算符是 ...
,而不是逗号。
熟练的 shell 程序员应该注意以下几点
反引号运算符进行变量插值,而不考虑命令中是否存在单引号。
反引号运算符不会对返回值进行任何转换,这与 csh 不同。
Shell(尤其是 csh)在每个命令行上执行多个级别的替换。Perl 仅在某些结构中进行替换,例如双引号、反引号、尖括号和搜索模式。
Shell 一次解释一小部分脚本。Perl 在执行之前编译整个程序(除了 BEGIN
块,它们在编译时执行)。
参数可以通过 @ARGV 获取,而不是 $1、$2 等。
环境不会自动作为单独的标量变量提供。
Shell 的 test
使用 "=", "!=", "<" 等进行字符串比较,使用 "-eq"、"-ne"、"-lt" 等进行数值比较。这与 Perl 相反,Perl 使用 eq
、ne
、lt
进行字符串比较,使用 ==
、!=
<
等进行数值比较。
正在学习 Perl 编程的程序员应该注意以下几点
请记住,许多操作在列表上下文中和标量上下文中表现不同。有关详细信息,请参阅 perldata。
如果可以,请避免使用裸词,尤其是全小写字母的裸词。你无法仅凭外观判断一个裸词是函数还是字符串。通过在字符串上使用引号,在函数调用上使用括号,你永远不会将它们混淆。
你无法仅仅通过检查来区分哪些内置函数是一元运算符(如 chop() 和 chdir()),哪些是列表运算符(如 print() 和 unlink())。(除非原型化,用户定义的子例程只能是列表运算符,而不能是一元运算符。)请参阅 perlop 和 perlsub。
人们很难记住某些函数默认使用 $_、@ARGV 或其他变量,而另一些你可能期望使用的函数却没有。
<FH> 结构不是文件句柄的名称,而是对该句柄的 readline 操作。只有当文件读取是 while 循环中的唯一条件时,读取的数据才会被分配给 $_。
while (<FH>) { }
while (defined($_ = <FH>)) { }..
<FH>; # data discarded!
请记住,当你需要 =~
时不要使用 =
;这两个结构完全不同。
$x = /foo/;
$x =~ /foo/;
do {}
结构不是一个真正的循环,你不能对其使用循环控制。
只要可以,请使用 my()
来声明局部变量(但请参阅 perlform 以了解哪些情况不可使用)。使用 local()
实际上为全局变量提供了一个局部值,这会让你容易受到动态作用域的不可预见副作用的影响。
如果你在模块中对一个导出的变量进行局部化,它的导出值不会改变。局部名称成为新值的别名,但外部名称仍然是原始值的别名。
与往常一样,如果这些问题中的任何一个被正式声明为 bug,它们将被修复并移除。