内容

名称

perldata - Perl 数据类型

描述

变量名

Perl 有三种内置数据类型:标量、标量数组和标量关联数组,称为“哈希”。标量是单个字符串(任何大小,仅受可用内存限制)、数字或对某物的引用(将在 perlref 中讨论)。普通数组是有序的标量列表,由数字索引,从 0 开始。哈希是无序的标量值集合,由其关联的字符串键索引。

通常,值通过名称或命名引用来引用。名称的第一个字符告诉您它指的是哪种数据结构。名称的其余部分告诉您它指的是哪个特定值。通常,这个名称是一个简单的标识符,即以字母或下划线开头,包含字母、下划线和数字的字符串。在某些情况下,它可能是一串由::(或已弃用的')分隔的标识符;除了最后一个之外,所有标识符都被解释为包的名称,用于定位查找最终标识符的命名空间(有关详细信息,请参阅"perlmod 中的包")。有关标识符的更深入讨论,请参阅"标识符解析"。可以将一个简单的标识符替换为一个表达式,该表达式在运行时生成对该值的引用。这将在下面和perlref中详细介绍。

Perl 也有自己的内置变量,其名称不遵循这些规则。它们有奇怪的名称,因此不会意外地与您的正常变量冲突。与正则表达式括号部分匹配的字符串保存在仅包含数字的名称下,这些数字位于$之后(请参阅perlopperlre)。此外,一些提供 Perl 内部工作窗口的特殊变量的名称包含标点符号。这些在perlvar中进行了说明。

标量值始终以 '$' 命名,即使引用的是数组或哈希的一部分的标量。'$' 符号在语义上类似于英语中的“the”,因为它表示需要单个值。

$days		# the simple scalar value "days"
$days[28]		# the 29th element of array @days
$days{'Feb'}	# the 'Feb' value from hash %days
$#days		# the last index of array @days

整个数组(以及数组和哈希的切片)用 '@' 表示,这与英语中的“these”或“those”非常相似,因为它表示需要多个值。

@days		# ($days[0], $days[1],... $days[n])
@days[3,4,5]	# same as ($days[3],$days[4],$days[5])
@days{'a','c'}	# same as ($days{'a'},$days{'c'})

整个哈希用 '%' 表示。

%days		# (key1, val1, key2, val2 ...)

此外,子例程以初始 '&' 命名,尽管在不含糊的情况下这是可选的,就像英语中的“do”一词通常是多余的一样。符号表条目可以用初始 '*' 命名,但您现在(如果有的话)并不关心这一点。

每个变量类型都有自己的命名空间,就像几个非变量标识符一样。这意味着您可以放心地使用相同的名称来表示标量变量、数组或哈希——或者,就这一点而言,用于文件句柄、目录句柄、子例程名称、格式名称或标签。这意味着 $foo 和 @foo 是两个不同的变量。这也意味着 $foo[1] 是 @foo 的一部分,而不是 $foo 的一部分。这可能看起来有点奇怪,但没关系,因为它确实很奇怪。

因为变量引用总是以 '$'、'@' 或 '%' 开头,所以“保留”词实际上并不保留在变量名方面。但是,它们在标签和文件句柄方面是保留的,因为它们没有初始特殊字符。例如,你不能有一个名为“log”的文件句柄。提示:你可以说 open(LOG,'logfile') 而不是 open(log,'logfile')。使用大写文件句柄还可以提高可读性,并防止与未来的保留词冲突。大小写是重要的——“FOO”、“Foo”和“foo”都是不同的名称。以字母或下划线开头的名称也可以包含数字和下划线。

可以使用返回对适当类型的引用的表达式来替换这样的字母数字名称。有关此的描述,请参见 perlref

以数字开头的名称只能包含更多数字。不以字母、下划线、数字或插入符号开头的名称仅限于一个字符,例如 $%$$。(大多数这些单字符名称对 Perl 都有预定义的意义。例如,$$ 是当前进程 ID。并且所有此类名称都保留供 Perl 可能使用。)

标识符解析

直到 Perl 5.18,有效标识符的实际规则有点模糊。但是,一般来说,这里定义的任何内容都应该在以前版本的 Perl 上工作,而相反——在以前版本中有效的边缘情况,但这里没有定义——可能在较新版本上不起作用。作为重要的一点说明,请注意,以下内容仅适用于 Perl 源代码中找到的裸字标识符,不适用于通过符号引用引入的标识符,符号引用对它们的限制要少得多。如果在 use utf8; pragma 的影响下工作,则以下规则适用

/ (?[ ( \p{Word} & \p{XID_Start} ) + [_] ])
  (?[ ( \p{Word} & \p{XID_Continue} ) ]) *    /x

也就是说,一个“开始”字符后面跟着任意数量的“继续”字符。Perl 要求标识符中的每个字符也匹配 \w(这可以防止一些问题情况);并且 Perl 还接受以下划线开头的标识符名称。

如果不使用 use utf8,则源代码将被视为 ASCII + 128 个额外的通用字符,并且标识符应匹配

/ (?aa) (?!\d) \w+ /x

也就是说,ASCII 范围内的任何单词字符,只要第一个字符不是数字。

Perl 中有两种包分隔符:双冒号 (::) 和单引号 (')。使用 ' 作为包分隔符已过时,将在 Perl 5.40 中移除。普通标识符可以以双冒号开头或结尾,并且可以包含多个由双冒号分隔的部分。单引号具有类似的规则,但例外的是它们不能出现在标识符的末尾:也就是说,$'foo$foo'bar 是合法的,但 $foo'bar' 不合法。

此外,如果标识符前面有 sigil(即,如果标识符是变量名的一部分),则可以选择将其括在花括号中。

虽然可以将双冒号与单引号混合使用,但引号必须放在冒号之后:$::::'foo$foo::'bar 是合法的,但 $::'::foo$foo'::bar 不合法。

综合起来,匹配基本标识符的语法如下:

/
 (?(DEFINE)
     (?<variable>
         (?&sigil)
         (?:
                 (?&normal_identifier)
             |   \{ \s* (?&normal_identifier) \s* \}
         )
     )
     (?<normal_identifier>
         (?: :: )* '?
          (?&basic_identifier)
          (?: (?= (?: :: )+ '? | (?: :: )* ' ) (?&normal_identifier) )?
         (?: :: )*
     )
     (?<basic_identifier>
       # is use utf8 on?
         (?(?{ (caller(0))[8] & $utf8::hint_bits })
             (?&Perl_XIDS) (?&Perl_XIDC)*
           | (?aa) (?!\d) \w+
         )
     )
     (?<sigil> [&*\$\@\%])
     (?<Perl_XIDS> (?[ ( \p{Word} & \p{XID_Start} ) + [_] ]) )
     (?<Perl_XIDC> (?[ \p{Word} & \p{XID_Continue} ]) )
 )
/x

同时,特殊标识符不遵循上述规则;在大多数情况下,此类标识符中的所有标识符都具有 Perl 给定的特殊含义。由于它们具有特殊的解析规则,因此通常不能完全限定。它们有六种形式(但不要使用形式 5 和 6)

  1. 一个 sigil,后面仅跟着与 \p{POSIX_Digit} 匹配的数字,例如 $0$1$10000

  2. 一个 sigil,后面跟着与 \p{POSIX_Punct} 属性匹配的单个字符,例如 $!%+,但字符 "{" 不起作用。

  3. 一个 sigil,后面跟着一个插入符号和任何一个字符 [][A-Z^_?\],例如 $^V$^]

  4. 类似于上面,一个 sigil,后面跟着花括号中的裸词文本,其中第一个字符是插入符号。下一个字符是 [][A-Z^_?\] 中的任何一个字符,后面跟着 ASCII 字词字符。例如 ${^GLOBAL_PHASE}

  5. 一个 sigil,后面跟着 [\xA1-\xAC\xAE-\xFF] 范围内的任何单个字符,前提是不在 "use utf8" 下。(在 "use utf8" 下,本节前面给出的普通标识符规则适用。)从 v5.26.0 开始,不允许使用非图形字符(C1 控制字符、不间断空格和软连字符)。使用其他字符是不明智的,因为这些字符都保留用于对 Perl 具有特殊含义,并且目前没有一个具有特殊含义,尽管这可能会在未经通知的情况下发生变化。

    请注意,这种形式的含义是,有些标识符仅在 "use utf8" 下合法,反之亦然,例如标识符 $état"use utf8" 下合法,但在其他情况下被认为是单个字符变量 后面跟着裸词 "tat",它们的组合是语法错误。

  6. 这是前两种形式的组合。它仅在不在 "use utf8" 下时有效(在 "use utf8" 下,普通标识符规则适用)。这种形式是一个 sigil,后面跟着花括号中的文本,其中第一个字符是 [\x80-\xFF] 范围内的任何一个字符,后面跟着 ASCII 字词字符,直到尾随的花括号。

    与之前形式相同的警告适用:非图形字符不再允许使用 "use utf8",完全不使用此形式是不明智的,并且 utf8 会产生很大影响。

在 Perl v5.24 之前,非图形 ASCII 控制字符在某些情况下也被允许;自 v5.20 起,此功能已弃用。

上下文

Perl 中操作和值的解释有时取决于操作或值周围上下文的需要。主要有两种上下文:列表和标量。某些操作在需要列表的上下文中返回列表值,而在其他情况下返回标量值。如果操作确实如此,则会在该操作的文档中提及。换句话说,Perl 根据预期返回值是单数还是复数来重载某些操作。英语中有些词语也是这样,比如“鱼”和“羊”。

反过来,操作为其每个参数提供标量或列表上下文。例如,如果你说

int( <STDIN> )

整数操作为 <> 运算符提供标量上下文,该运算符会从 STDIN 读取一行并将其传递回整数操作,然后整数操作会找到该行的整数值并返回该值。另一方面,如果你说

sort( <STDIN> )

那么 sort 操作为 <> 提供列表上下文,它将继续读取直到文件结尾的所有可用行,并将该行列表传递回 sort 例程,然后 sort 例程将对这些行进行排序并将它们作为列表返回给 sort 的上下文。

赋值有点特殊,因为它使用其左参数来确定右参数的上下文。对标量的赋值会以标量上下文评估右侧,而对数组或哈希的赋值会以列表上下文评估右侧。对列表(或切片,它本身就是一个列表)的赋值也会以列表上下文评估右侧。

当你使用 use warnings 编译指示或 Perl 的 -w 命令行选项时,你可能会看到关于在“空上下文”中无用地使用常量或函数的警告。空上下文只是意味着该值已被丢弃,例如仅包含 "fred";getpwuid(0); 的语句。对于关心是否在列表上下文中被调用的函数,它仍然算作标量上下文。

用户定义的子例程可以选择关心它们是在空、标量还是列表上下文中被调用。大多数子例程不需要费心。这是因为标量和列表都会自动插入列表中。有关如何动态识别函数的调用上下文的详细信息,请参阅 "perlfunc 中的 wantarray"

标量值

Perl 中的所有数据都是标量、标量数组或标量哈希。标量可以包含三种不同类型中的一个单一值:数字、字符串或引用。通常,从一种形式转换为另一种形式是透明的。虽然标量不能直接保存多个值,但它可以包含对数组或哈希的引用,而数组或哈希又包含多个值。

标量不一定是某一特定类型。没有地方可以声明标量变量为“字符串”类型、“数字”类型、“引用”类型或其他任何类型。由于标量的自动转换,返回标量的操作不需要(事实上,也不能)关心它们的调用者正在寻找字符串、数字还是引用。Perl 是一种上下文多态语言,其标量可以是字符串、数字或引用(包括对象)。虽然字符串和数字在几乎所有情况下都被视为相同的东西,但引用是强类型、不可转换的指针,具有内置的引用计数和析构函数调用。

如果标量值为未定义、空字符串或数字 0(或其字符串等效项“0”),则在布尔意义上被解释为 FALSE,如果为其他任何值,则被解释为 TRUE。布尔上下文只是标量上下文的一种特殊情况,其中永远不会执行转换为字符串或数字的操作。使用 !not 对真值进行否定会返回一个特殊的假值。当作为字符串进行评估时,它被视为 "",但作为数字时,它被视为 0。大多数返回真或假的 Perl 运算符的行为方式与此相同。

实际上有两种空字符串(有时称为“空”字符串),一种是已定义的,另一种是未定义的。已定义的版本只是一个长度为零的字符串,例如 ""。未定义的版本是表示某事物没有实际值的 value,例如发生错误时、文件末尾或引用未初始化的变量或数组或哈希的元素时。虽然在早期版本的 Perl 中,未定义的标量在第一次在需要已定义值的 place 使用时会变得已定义,但这种情况不再发生,除非在 perlref 中解释的自动生成的情况下。

要确定给定字符串是否为有效的非零数字,有时只需将其与数字 0 和词法“0”进行比较即可(尽管如果启用了警告,这会导致噪音)。这是因为不是数字的字符串被视为 0,就像它们在 awk 中一样。

    if ($str == 0 && $str ne "0")  {
	warn "That doesn't look like a number";
    }

这种方法可能是最好的,因为否则你将无法正确处理 IEEE 表示法,如 NaNInfinity。在其他情况下,你可能更喜欢通过调用 POSIX::strtod() 函数或使用正则表达式检查你的字符串来确定字符串数据是否可以用于数值(如 perlre 中所述)。

    warn "has nondigits"	if     /\D/;
    warn "not a natural number" unless /^\d+$/;             # rejects -3
    warn "not an integer"       unless /^-?\d+$/;           # rejects +3
    warn "not an integer"       unless /^[+-]?\d+$/;
    warn "not a decimal number" unless /^-?\d+\.?\d*$/;     # rejects .2
    warn "not a decimal number" unless /^-?(?:\d+(?:\.\d*)?|\.\d+)$/;
    warn "not a C float"
	unless /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/;

数组的长度是一个标量值。您可以通过评估 $#days 来查找数组 @days 的长度,就像在 csh 中一样。但是,这不是数组的长度;它是最后一个元素的下标,这是一个不同的值,因为通常有一个第 0 个元素。将值赋给 $#days 实际上会改变数组的长度。以这种方式缩短数组会破坏中间的值。延长之前缩短的数组不会恢复那些元素中的值。

您还可以通过预先扩展将要变大的数组来获得一些微不足道的效率提升。您还可以通过将值赋给数组末尾之外的元素来扩展数组。您可以通过将空列表 () 赋给数组来将其截断为空。以下是等效的

@whatever = ();
$#whatever = -1;

如果您在标量上下文中评估数组,它将返回数组的长度。(请注意,这对于列表来说是不正确的,列表返回最后一个值,就像 C 逗号运算符一样,也不适用于内置函数,内置函数返回它们想要返回的任何值。)以下始终为真

scalar(@whatever) == $#whatever + 1;

一些程序员选择使用显式转换,以免留下任何疑问

$element_count = scalar(@whatever);

如果您在标量上下文中评估哈希,如果哈希为空,它将返回一个假值。如果有任何键值对,它将返回一个真值。更精确的定义取决于版本。

在 Perl 5.25 之前,返回的值是一个字符串,由已用桶的数量和已分配桶的数量组成,用斜杠分隔。这几乎只用于找出 Perl 的内部哈希算法在您的数据集上是否执行不佳。例如,您在哈希中插入 10,000 个东西,但在标量上下文中评估 %HASH 会显示 "1/16",这意味着只有 16 个桶中的一个被触及,并且可能包含所有 10,000 个项目。这本不应该发生。

从 Perl 5.25 开始,返回值更改为哈希中键的计数。如果您需要访问旧的行为,可以使用 Hash::Util::bucket_ratio() 代替。

如果在标量上下文中评估绑定哈希,则会调用 SCALAR 方法(并回退到 FIRSTKEY)。

您可以通过将值赋给 keys() 函数来为哈希预分配空间。这会将已分配的桶向上舍入到下一个 2 的幂

keys(%users) = 1000;		# allocate 1024 buckets

标量值构造器

数字字面量可以用以下任何一种浮点数或整数格式指定

12345
12345.67
.23E-10             # a very small number
3.14_15_92          # a very important number
4_294_967_296       # underscore for legibility
0xff                # hex
0xdead_beef         # more hex
0377                # octal (only numbers, begins with 0)
0o12_345            # alternative octal (introduced in Perl 5.33.5)
0b011011            # binary
0x1.999ap-4         # hexadecimal floating point (the 'p' is required)

您可以在数字字面量中的数字之间使用下划线(下划线)来提高可读性(但不能连续使用多个下划线:23__500 不合法;23_500 是合法的)。例如,您可以将二进制数字分组为三个(如 Unix 风格的模式参数 0b110_100_100)或四个(表示 nibble,如 0b1010_0110)或其他组。

字符串字面量通常用单引号或双引号分隔。它们的工作方式与标准 Unix shell 中的引号非常相似:双引号字符串字面量会受到反斜杠和变量替换的影响;单引号字符串不会(除了 \'\\)。通常的 C 风格反斜杠规则适用于创建换行符、制表符等字符,以及一些更奇特的形式。有关列表,请参见 "perlop 中的引号和类似引号的操作符"

字符串字面量中的十六进制、八进制或二进制表示形式(例如 '0xff')不会自动转换为它们的整数表示形式。hex() 和 oct() 函数为您执行这些转换。有关更多详细信息,请参见 "perlfunc 中的 hex""perlfunc 中的 oct"

十六进制浮点数可以像十六进制字面量一样开始,并且可以后跟可选的十六进制小数部分,但必须后跟 p、可选的符号和二的幂。该格式对于准确地表示浮点值很有用,避免了与十进制浮点数的转换,从而避免了可能的精度损失。请注意,虽然大多数当前平台使用 64 位 IEEE 754 浮点数,但并非所有平台都使用。另一个潜在的(低阶)差异来源是浮点舍入模式,这些模式在 CPU、操作系统和编译器之间可能有所不同,而 Perl 无法控制这些模式。

您也可以直接在字符串中嵌入换行符,即它们可以在不同的行结束,而不是在它们开始的地方。这很好,但是如果您忘记了尾部的引号,则错误不会在 Perl 找到包含引号字符的另一行之前报告,这可能在脚本中更远的地方。字符串内部的变量替换仅限于标量变量、数组和数组或哈希切片。(换句话说,以 $ 或 @ 开头的名称,后面跟着可选的方括号表达式作为下标。)以下代码段打印出“价格是 $100”。

$Price = '$100';	# not interpolated
print "The price is $Price.\n";	# interpolated

Perl 中没有双重插值,因此 $100 保持原样。

默认情况下,在字符串内替换的浮点数使用点(“.”)作为小数点分隔符。如果 use locale 生效,并且已调用 POSIX::setlocale(),则用于小数点分隔符的字符会受到 LC_NUMERIC 本地化的影响。请参阅 perllocalePOSIX

使用大括号分隔的变量名称

与某些 shell 一样,您可以将变量名括在大括号中作为分隔符,以将其与后面的字母数字和下划线或其他文本区分开来。在将变量插值到字符串中时,您也必须这样做,以将变量名与后面的双冒号或撇号分隔开,因为这些符号将被视为包分隔符。

$who = "Larry";
print PASSWD "${who}::0:0:Superuser:/:/bin/perl\n";
print "We use ${who}speak when ${who}'s here.\n";

如果没有大括号,Perl 将查找 $whospeak、$who::0$who's 变量。最后两个将是(可能不存在的)包 who 中的 $0 和 $s 变量。

事实上,这种大括号内的简单标识符将强制转换为字符串,哈希下标内也是如此。两者都不需要引号。我们之前的示例 $days{'Feb'} 可以写成 $days{Feb},引号将自动添加。但是下标中任何更复杂的内容都将被解释为表达式。这意味着例如 $version{2.0}++ 等效于 $version{2}++,而不是 $version{'2.0'}++

与看起来像数组或哈希访问符号的文本进行插值时,存在类似的问题。将像 $who 这样的简单变量直接放在像 "[1]""{foo}" 这样的文本前面会导致变量被插值,就像访问 @who 的元素或存储在 %who 中的值一样。

$who = "Larry Wall";
print "$who[1] is the father of Perl.\n";

会尝试访问名为 @who 的数组的索引 1。同样,使用大括号可以防止这种情况发生

$who = "Larry Wall";
print "${who}[1] is the father of Perl.\n";

将被视为与

$who = "Larry Wall";
print $who . "[1] is the father of Perl.\n";

这种表示法也适用于更复杂的变量描述,例如带下标的数组或哈希访问。例如

@name = qw(Larry Curly Moe);
print "Also ${name[0]}[1] was a member\n";

如果没有大括号,上面的例子将被解析为 @name 数组中的二级数组下标,并且在 use strict 下可能会产生致命异常,因为它将被解析为

print "Also " . $name[0][1] . " was a member\n";

而不是预期的

print "Also " . $name[0] . "[1] was a member\n";

类似的结果可以通过在不属于要访问的变量的下标或包表示法的第一个字符上使用反斜杠来获得。因此,上面的例子也可以写成

@name = qw(Larry Curly Moe);
print "Also $name[0]\[1] was a member\n";

但是对于某些特殊变量(多字符插入符号变量),使用大括号的界定形式是您唯一可以引用该变量的方式,也是您唯一可以通过插值访问该变量下标的方式。

考虑魔法数组 @{^CAPTURE},它由正则表达式引擎填充,包含模式中所有捕获缓冲区的内容(参见 perlvarperlre)。您唯一可以在字符串中访问这些成员之一的方式是通过带括号(界定)的形式

"abc"=~/(.)(.)(.)/
    and print "Second buffer is ${^CAPTURE[1]}";

等同于

"abc"=~/(.)(.)(.)/
    and print "Second buffer is " . ${^CAPTURE}[1];

@^CAPTURE 是语法错误,因此它必须被引用为 @{^CAPTURE},并且要在普通代码中访问其元素之一,您需要编写 ${^CAPTURE}[1] 。但是,当在字符串中进行插值时,"${^CAPTURE}[1]" 将等同于 ${^CAPTURE} . "[1]",这甚至不引用同一个变量!因此,下标也必须放在大括号里面"${^CAPTURE[1]}"

使用大括号的界定形式可以与所有不同类型的变量访问一起使用,包括数组和哈希切片。例如,以下代码

@name = qw(Larry Curly Moe);
local $" = " and ";
print "My favorites were @{name[1,2]}.\n";

将输出

My favorites were Curly and Moe.

特殊浮点数:无穷大 (Inf) 和非数字 (NaN)

浮点数包括特殊值 InfNaN,分别表示无穷大和非数字。无穷大也可以是负数。

无穷大是某些数学运算的结果,这些运算会导致浮点数范围溢出,例如 9**9**9。非数字是结果未定义或不可表示时的结果。但请注意,您无法从某些常见的“未定义”或“超出范围”运算(例如除以零或负数的平方根)获得 NaN,因为 Perl 会为这些运算生成致命错误。

无穷大和非数字有其特殊的算术规则。一般规则是它们是“传染性的”:Inf 加 1 等于 InfNaN 加 1 等于 NaN。有趣的是,当您将无穷大和非数字组合在一起时:Inf 减去 InfInf 除以 Inf 等于 NaN(而 InfInf 等于 InfInf 乘以 Inf 等于 Inf)。NaN 也很有趣,因为它不等于任何数字,包括它本身:NaN != NaN

Perl 不理解 InfNaN 作为数字字面量,但您可以将它们作为字符串,Perl 会根据需要进行转换:"Inf" + 1。(但是,您可以从 POSIX 扩展导入它们;use POSIX qw(Inf NaN);,然后将它们用作字面量。)

请注意,在输入(字符串到数字)时,Perl 接受 InfNaN 的多种形式。大小写被忽略,并且理解 Win32 特定的形式,例如 1.#INF,但在输出时,值会被规范化为 InfNaN

版本字符串

形式为 v1.20.300.4000 的字面量被解析为一个字符串,该字符串由具有指定序数的字符组成。这种形式,称为 v-字符串,提供了一种替代的、更易读的方式来构造字符串,而不是使用不太易读的插值形式 "\x{1}\x{14}\x{12c}\x{fa0}"。这对于表示 Unicode 字符串以及使用字符串比较运算符(cmpgtlt 等)比较版本“数字”很有用。如果字面量中有两个或多个点,则可以省略前导 v

print v9786;              # prints SMILEY, "\x{263a}"
print v102.111.111;       # prints "foo"
print 102.111.111;        # same

requireuse 都接受此类字面量来进行版本检查。请注意,除非您还使用 Socket 包的 inet_aton()/inet_ntoa() 例程,否则将 v-字符串用于 IPv4 地址不可移植。

请注意,从 Perl 5.8.1 开始,单数字 v 字符串(如 v65)在 => 运算符(通常用于分隔哈希键和哈希值)之前不再是 v 字符串;而是被解释为字面字符串('v65')。在 Perl 5.6.0 到 Perl 5.8.0 中,它们是 v 字符串,但这会导致更多混乱和破坏,而不是好处。多数字 v 字符串,如 v65.6665.66.67,始终保持 v 字符串。

特殊字面量

特殊字面量 __FILE__、__LINE__ 和 __PACKAGE__ 分别代表程序中该点处的当前文件名、行号和包名。__SUB__ 给出对当前子例程的引用。它们只能用作单独的标记;它们不会被插入到字符串中。如果当前没有包(由于空 package; 指令),__PACKAGE__ 是未定义的值。(但空 package; 从 5.10 版本开始不再受支持。)在子例程之外,__SUB__ 是未定义的值。__SUB__ 仅在 5.16 或更高版本中可用,并且仅在使用 use v5.16use feature "current_sub" 声明时可用。

两个控制字符 ^D 和 ^Z,以及标记 __END__ 和 __DATA__ 可用于指示脚本的逻辑结束,而不是实际的文件结束。解释器会忽略任何后续文本,除非程序按如下所述读取。

__DATA__ 之后的文本可以通过文件句柄 PACKNAME::DATA 读取,其中 PACKNAME 是遇到 __DATA__ 标记时当前的包。文件句柄保持打开状态,指向 __DATA__ 之后的行。程序在完成读取后应该 close DATA。(如果模块因任何原因重新加载,将其保持打开状态会导致文件句柄泄漏,因此关闭它是一种更安全的做法。)为了与在引入 __DATA__ 之前编写的旧脚本兼容,__END__ 在顶层脚本中(但在使用 requiredo 加载的文件中除外)的行为类似于 __DATA__,并将文件的剩余内容保留在 main::DATA 中。

while (my $line = <DATA>) { print $line; }
close DATA;
__DATA__
Hello world.

默认情况下,DATA 文件句柄具有 Perl 在读取文件以解析源代码时所使用的任何 PerlIO 层。通常这意味着文件正在逐字节读取,就像它以 Latin-1 编码一样,但有两种主要方式可以使其不同。首先,如果 __END__/__DATA__ 标记在 use utf8 编译指示的范围内,则 DATA 句柄将处于 UTF-8 模式。其次,如果源代码是从 perl 的标准输入读取的,则 DATA 文件句柄实际上是 STDIN 文件句柄的别名,并且可能处于 UTF-8 模式,因为 PERL_UNICODE 环境变量或 perl 的命令行开关。

有关 __DATA__ 的更多描述以及使用示例,请参阅 SelfLoader。请注意,您不能在 BEGIN 块中从 DATA 文件句柄读取:BEGIN 块在看到它时立即执行(在编译期间),此时相应的 __DATA__(或 __END__)标记尚未看到。

裸词

在语法中没有其他解释的词将被视为被引用的字符串。这些被称为“裸词”。与文件句柄和标签一样,完全由小写字母组成的裸词可能会与将来的保留字冲突,如果您使用 use warnings 编译指示或 -w 开关,Perl 会警告您有关任何此类词。Perl 将裸词(如标识符)限制为大约 250 个字符。未来的 Perl 版本可能会消除这些任意限制。

有些人可能希望完全禁止裸词。如果您说

use strict 'subs';

如果任何裸字不能被解释为子程序调用,则会产生编译时错误。此限制持续到封闭块的末尾。内部块可以通过 no strict 'subs' 来抵消此限制。

数组插值

数组和切片通过使用 $" 变量(如果指定了 "use English;",则为 $LIST_SEPARATOR)指定的定界符连接元素,默认情况下为空格,插值到双引号字符串中。以下等效

$temp = join($", @ARGV);
system "echo $temp";

system "echo @ARGV";

在搜索模式中(也进行双引号替换),存在一个不幸的歧义:/$foo[bar]/ 应该解释为 /${foo}[bar]/(其中 [bar] 是正则表达式的字符类)还是 /${foo[bar]}/(其中 [bar] 是数组 @foo 的下标)?如果 @foo 不存在,那么它显然是一个字符类。如果 @foo 存在,Perl 会对 [bar] 进行合理的猜测,并且几乎总是正确的。如果它猜错了,或者你只是单纯地偏执,你可以使用花括号强制执行正确的解释,如上所示。

如果你正在寻找有关如何使用 here-documents 的信息,这些信息曾经在这里,现在已移至 "perlop 中的引号和类似引号的操作符"

列表值构造器

列表值通过用逗号分隔各个值(并在需要优先级时用括号括起来)来表示。

(LIST)

在不需要列表值的上下文中,看起来像列表字面量的值只是最后一个元素的值,就像 C 语言中的逗号运算符一样。例如,

@foo = ('cc', '-E', $bar);

将整个列表值分配给数组 @foo,但是

$foo = ('cc', '-E', $bar);

将变量 $bar 的值分配给标量变量 $foo。请注意,实际数组在标量上下文中的值是数组的长度;以下将值 3 分配给 $foo

@foo = ('cc', '-E', $bar);
$foo = @foo;                # $foo gets 3

你可以在列表字面量的结束括号之前有一个可选的逗号,这样你就可以说

@foo = (
    1,
    2,
    3,
);

要使用 here-document 为数组分配值,每行一个元素,你可以使用以下方法

@sauces = <<End_Lines =~ m/(\S.*\S)/g;
    normal tomato
    spicy tomato
    green chile
    pesto
    white wine
End_Lines

LIST 会自动插值子列表。也就是说,当评估 LIST 时,列表的每个元素都在列表上下文中进行评估,并且生成的列表值被插值到 LIST 中,就像每个单独的元素都是 LIST 的成员一样。因此,数组和哈希在 LIST 中会失去其身份——列表

(@foo,@bar,&SomeSub,%glarch)

包含 @foo 的所有元素,然后是 @bar 的所有元素,然后是按列表上下文调用的名为 SomeSub 的子程序返回的所有元素,然后是 %glarch 的键值对。要创建不插值的列表引用,请参见 perlref

空列表用 () 表示。在列表中插值它不会产生任何影响。因此 ((),(),()) 等效于 ()。类似地,插值一个没有元素的数组与在该点没有插值任何数组相同。

这种插值结合了以下事实:开括号和闭括号是可选的(除非在优先级需要时除外),列表可以以可选的逗号结尾,这意味着列表中的多个逗号是合法的语法。列表 1,,3 是两个列表 1,3 的串联,第一个列表以可选的逗号结尾。1,,3 等于 (1,),(3) 等于 1,3(类似地,1,,,3 等于 (1,),(,),3 等于 1,3 等等)。请注意,我们不建议您使用这种混淆方式。

列表值也可以像普通数组一样使用下标访问。为了避免歧义,您必须将列表放在括号中。例如

# Stat returns list value.
$time = (stat($file))[8];

# SYNTAX ERROR HERE.
$time = stat($file)[8];  # OOPS, FORGOT PARENTHESES

# Find a hex digit.
$hexdigit = ('a','b','c','d','e','f')[$digit-10];

# A "reverse comma operator".
return (pop(@foo),pop(@foo))[0];

列表只能在每个元素本身都是合法赋值目标时才能被赋值。

($x, $y, $z) = (1, 2, 3);

($map{'red'}, $map{'blue'}, $map{'green'}) = (0x00f, 0x0f0, 0xf00);

一个例外是,您可以在列表中将 undef 赋值给某个元素。这对于丢弃函数的某些返回值很有用。

($dev, $ino, undef, undef, $uid, $gid) = stat($file);

从 Perl 5.22 开始,您也可以使用 (undef)x2 代替 undef, undef。(您也可以使用 ($x) x 2,但这不太有用,因为它会对同一个变量赋值两次,覆盖第一次赋值的值。)

当您将一个标量列表赋值给一个数组时,该数组中的所有先前值都会被清除,并且数组中的元素数量将等于右侧列表中的元素数量——即进行赋值的列表。数组将自动调整大小以精确地容纳右侧列表中的每个元素。

use warnings;
my (@xyz, $x, $y, $z);

@xyz = (1, 2, 3);
print "@xyz\n";                             # 1 2 3

@xyz = ('al', 'be', 'ga', 'de');
print "@xyz\n";                             # al be ga de

@xyz = (101, 102);
print "@xyz\n";                             # 101 102

但是,当您将一个标量列表赋值给另一个标量列表时,结果会根据左侧列表(被赋值的列表)的元素数量是与右侧列表相同、更多还是更少而有所不同。

($x, $y, $z) = (1, 2, 3);
print "$x $y $z\n";                         # 1 2 3

($x, $y, $z) = ('al', 'be', 'ga', 'de');
print "$x $y $z\n";                         # al be ga

($x, $y, $z) = (101, 102);
print "$x $y $z\n";                         # 101 102
# Use of uninitialized value $z in concatenation (.)
# or string at [program] line [line number].

如果左侧列表中的标量数量少于右侧列表中的标量数量,则右侧列表中的“额外”标量将不会被赋值。

如果左侧列表中的标量数量多于右侧列表中的标量数量,则“缺失”的标量将变为未定义。

($x, $y, $z) = (101, 102);
for my $el ($x, $y, $z) {
    (defined $el) ? print "$el " : print "<undef>";
}
print "\n";
                                            # 101 102 <undef>

在标量上下文中,列表赋值返回右侧赋值表达式产生的元素数量。

$x = (($foo,$bar) = (3,2,1));       # set $x to 3, not 2
$x = (($foo,$bar) = f());           # set $x to f()'s return count

当您想要在布尔上下文中进行列表赋值时,这非常有用,因为大多数列表函数在完成时会返回一个空列表,在赋值时会产生 0,这被解释为 FALSE。

这也是一个有用习惯用语的来源,用于在列表上下文中执行函数或操作,然后计算返回值的数量,方法是将赋值给一个空列表,然后在标量上下文中使用该赋值。例如,以下代码

$count = () = $string =~ /\d+/g;

将 $string 中找到的数字组数量放入 $count 中。这是因为模式匹配是在列表上下文中进行的(因为它被分配给空列表),因此将返回字符串中所有匹配部分的列表。标量上下文中的列表赋值将将其转换为元素数量(这里,模式匹配的次数)并将其分配给 $count。请注意,简单地使用

$count = $string =~ /\d+/g;

将不起作用,因为标量上下文中的模式匹配只会返回真或假,而不是匹配次数。

列表赋值的最后一个元素可以是数组或哈希

($x, $y, @rest) = split;
my($x, $y, %rest) = @_;

实际上可以在列表中的任何位置放置数组或哈希,但列表中的第一个将吸收所有值,而它后面的任何内容都将变为未定义。这在 my() 或 local() 中可能很有用。

可以使用包含要解释为键和值的成对项目的文字列表来初始化哈希

# same as map assignment above
%map = ('red',0x00f,'blue',0x0f0,'green',0xf00);

虽然文字列表和命名数组通常可以互换,但哈希并非如此。仅仅因为你可以像普通数组一样对列表值进行下标,并不意味着你可以像哈希一样对列表值进行下标。同样,作为其他列表(包括函数的参数列表和返回值列表)的一部分包含的哈希总是会展平为键/值对。这就是为什么有时使用引用会更好。

在键/值对之间使用 => 运算符通常更具可读性。=> 运算符主要只是逗号的视觉上更具区别的同义词,但它还安排其左侧操作数被解释为字符串,如果它是一个裸字,它将是一个合法的简单标识符。=> 不会引用包含双冒号的复合标识符。这使得它非常适合初始化哈希

 %map = (
              red   => 0x00f,
              blue  => 0x0f0,
              green => 0xf00,
);

或用于初始化用作记录的哈希引用

$rec = {
            witch => 'Mable the Merciless',
            cat   => 'Fluffy the Ferocious',
            date  => '10/31/1776',
};

或用于对复杂函数使用按名称参数调用

$field = $query->radio_group(
            name      => 'group_name',
            values    => ['eenie','meenie','minie'],
            default   => 'meenie',
            linebreak => 'true',
            labels    => \%labels
);

请注意,仅仅因为哈希按此顺序初始化并不意味着它按此顺序输出。有关如何安排输出顺序的示例,请参见 "perlfunc 中的 sort"

如果一个键在哈希的初始化列表中出现多次,则最后一次出现获胜

%circle = (
              center => [5, 10],
              center => [27, 9],
              radius => 100,
              color => [0xDF, 0xFF, 0x00],
              radius => 54,
);

# same as
%circle = (
              center => [27, 9],
              color => [0xDF, 0xFF, 0x00],
              radius => 54,
);

这可用于提供可覆盖的配置默认值

# values in %args take priority over %config_defaults
%config = (%config_defaults, %args);

下标

可以通过指定一个美元符号 ($),然后是数组的名称(不带前导 @),然后是方括号内的下标来一次访问一个数组的标量。例如

@myarray = (5, 50, 500, 5000);
print "The Third Element is", $myarray[2], "\n";

数组索引从 0 开始。负索引从数组末尾开始检索值。在我们的示例中,$myarray[-1] 将是 5000,而 $myarray[-2] 将是 500。

哈希索引类似,只是使用花括号而不是方括号。例如

%scientists = 
(
    "Newton" => "Isaac",
    "Einstein" => "Albert",
    "Darwin" => "Charles",
    "Feynman" => "Richard",
);

print "Darwin's First Name is ", $scientists{"Darwin"}, "\n";

您还可以对列表进行索引以获取其中的单个元素

$dir = (getpwnam("daemon"))[7];

多维数组模拟

可以使用列表对哈希进行索引来模拟多维数组。列表的元素将与索引分隔符连接(参见 "$;" in perlvar)。

$foo{$x,$y,$z}

等同于

$foo{join($;, $x, $y, $z)}

默认的索引分隔符是 "\034",与 awk 中的 SUBSEP 相同。

切片

切片使用索引列表同时访问列表、数组或哈希中的多个元素。它比将各个元素作为单独的标量值列表写出来更方便。

($him, $her)   = @folks[0,-1];              # array slice
@them          = @folks[0 .. 3];            # array slice
($who, $home)  = @ENV{"USER", "HOME"};      # hash slice
($uid, $dir)   = (getpwnam("daemon"))[2,7]; # list slice

由于您可以将值赋给变量列表,因此也可以将值赋给数组或哈希切片。

@days[3..5]    = qw/Wed Thu Fri/;
@colors{'red','blue','green'} 
               = (0xff0000, 0x0000ff, 0x00ff00);
@folks[0, -1]  = @folks[-1, 0];

前面的赋值与以下赋值完全相同

($days[3], $days[4], $days[5]) = qw/Wed Thu Fri/;
($colors{'red'}, $colors{'blue'}, $colors{'green'})
               = (0xff0000, 0x0000ff, 0x00ff00);
($folks[0], $folks[-1]) = ($folks[-1], $folks[0]);

由于更改切片会更改它所切片的原始数组或哈希,因此 foreach 结构将更改数组或哈希中的一些甚至所有值。

foreach (@array[ 4 .. 10 ]) { s/peter/paul/ } 

foreach (@hash{qw[key1 key2]}) {
    s/^\s+//;                       # trim leading whitespace
    s/\s+$//;                       # trim trailing whitespace
    s/\b(\w)(\w*)\b/\u$1\L$2/g;     # "titlecase" words
}

作为一个特殊情况,当您对列表(而不是数组或哈希)进行切片时,如果列表计算为空,那么对该空列表进行切片将始终依次产生空列表。因此

@a = ()[0,1];          # @a has no elements
@b = (@a)[0,1];        # @b has no elements
@c = (sub{}->())[0,1]; # @c has no elements
@d = ('a','b')[0,1];   # @d has two elements
@e = (@d)[0,1,8,9];    # @e has four elements
@f = (@d)[8,9];        # @f has two elements

这使得编写在返回空列表时终止的循环变得容易

while ( ($home, $user) = (getpwent)[7,0] ) {
    printf "%-8s %s\n", $user, $home;
}

如本文档前面所述,列表赋值的标量意义是赋值右侧元素的数量。空列表不包含任何元素,因此当密码文件耗尽时,结果为 0,而不是 2。

在标量上下文中,切片返回切片的最后一个项目。

@a = qw/first second third/;
%h = (first => 'A', second => 'B');
$t = @a[0, 1];                  # $t is now 'second'
$u = @h{'first', 'second'};     # $u is now 'B'

如果您对为什么在哈希切片上使用 '@' 而不是 '%' 感到困惑,可以这样想。括号类型(方括号或花括号)决定是查看数组还是哈希。另一方面,数组或哈希上的前导符号 ('$' 或 '@') 指示您是获取单个值(标量)还是多个值(列表)。

键值哈希切片

从 Perl 5.20 开始,使用 % 符号的哈希切片操作是切片操作的一种变体,它返回键值对列表,而不是仅返回值

%h = (blonk => 2, foo => 3, squink => 5, bar => 8);
%subset = %h{'foo', 'bar'}; # key/value hash slice
# %subset is now (foo => 3, bar => 8)
%removed = delete %h{'foo', 'bar'};
# %removed is now (foo => 3, bar => 8)
# %h is now (blonk => 2, squink => 5)

但是,这种切片的結果无法本地化或赋值。除此之外,它们与使用 @ 符号的哈希切片非常一致。

索引/值数组切片

类似于键/值哈希切片(也在 Perl 5.20 中引入),% 数组切片语法返回一个索引/值对列表

@a = "a".."z";
@list = %a[3,4,6];
# @list is now (3, "d", 4, "e", 6, "g")
@removed = delete %a[3,4,6]
# @removed is now (3, "d", 4, "e", 6, "g")
# @list[3,4,6] are now undef

请注意,强烈建议不要对数组值调用 delete

类型球和文件句柄

Perl 使用一种称为类型球的内部类型来保存整个符号表条目。类型球的类型前缀是 *,因为它代表所有类型。这曾经是将数组和哈希按引用传递给函数的首选方式,但现在我们有了真正的引用,这很少需要。

现代 Perl 中类型球的主要用途是创建符号表别名。此赋值

*this = *that;

使 $this 成为 $that 的别名,@this 成为 @that 的别名,%this 成为 %that 的别名,&this 成为 &that 的别名,等等。更安全的是使用引用。这

local *Here::blue = \$There::green;

暂时使 $Here::blue 成为 $There::green 的别名,但不会使 @Here::blue 成为 @There::green 的别名,或 %Here::blue 成为 %There::green 的别名,等等。有关此的更多示例,请参阅 "perlmod 中的符号表"。尽管这可能看起来很奇怪,但这是整个模块导入/导出系统的基础。

类型球的另一个用途是将文件句柄传递给函数或创建新的文件句柄。如果您需要使用类型球来保存文件句柄,请按以下方式进行

$fh = *STDOUT;

或者也许作为真正的引用,像这样

$fh = \*STDOUT;

有关在函数中将这些用作间接文件句柄的示例,请参阅 perlsub

类型球也是使用 local() 运算符创建本地文件句柄的一种方式。这些将持续到它们的块退出,但可以传递回来。例如

sub newopen {
    my $path = shift;
    local  *FH;  # not my!
    open   (FH, $path)          or  return undef;
    return *FH;
}
$fh = newopen('/etc/passwd');

现在我们有了 *foo{THING} 符号,类型球不再像以前那样用于文件句柄操作,尽管它们仍然需要将全新的文件和目录句柄传递进出函数。这是因为 *HANDLE{IO} 仅在 HANDLE 已用作句柄时才有效。换句话说,*FH 必须用于创建新的符号表条目;*foo{THING} 不能。如有疑问,请使用 *FH

所有能够创建文件句柄的函数(open()、opendir()、pipe()、socketpair()、sysopen()、socket() 和 accept())都会在传递给它们的句柄是未初始化的标量变量时自动创建一个匿名文件句柄。这允许使用诸如 open(my $fh, ...)open(local $fh,...) 之类的结构来创建文件句柄,这些文件句柄将在作用域结束时方便地自动关闭,前提是它们没有其他引用。这在很大程度上消除了在打开必须传递的文件句柄时对类型球的需求,如以下示例所示

sub myopen {
    open my $fh, "@_"
         or die "Can't open '@_': $!";
    return $fh;
}

{
    my $f = myopen("</etc/motd");
    print <$f>;
    # $f implicitly closed here
}

请注意,如果使用初始化的标量变量,结果将有所不同:my $fh='zzz'; open($fh, ...) 等效于 open( *{'zzz'}, ...)use strict 'refs' 禁止这种做法。

创建匿名文件句柄的另一种方法是使用 Symbol 模块或 IO::Handle 模块及其同类。这些模块的优点是不会在 local() 中隐藏相同名称的不同类型。有关示例,请参见 "perlfunc 中的 open" 的底部。

另请参阅

有关 Perl 内置变量的描述以及合法变量名称的讨论,请参见 perlvar。有关类型全局变量和 *foo{THING} 语法的更多讨论,请参见 perlrefperlsub"perlmod 中的符号表"