Skip to content

Perl 标量

标量(Scalar)是 Perl 的基本数据类型,用于表示单个值。这个值可以是一个数字(整数或浮点数)、一个字符串,或者一个引用(Reference,是一种指向其他数据结构的标量,例如数组、哈希或子例程)。标量变量以美元符号($)作为前缀。

这是一个展示标量变量的基本示例:

#!/usr/bin/perl
use strict;
use warnings;
my $age = 30; # 一个整数
my $name = "Alice Wonderland"; # 一个字符串
my $price = 19.99; # 一个浮点数
my $undefined_var; # 最初是 'undef'
print "Name: $name\n";
print "Age: $age\n";
print "Price: $price\n";
if (defined $undefined_var) {
print "Undefined Var: $undefined_var\n";
} else {
print "Undefined Var is undef\n";
}

这将产生以下结果:

Name: Alice Wonderland
Age: 30
Price: 19.99
Undefined Var is undef

数值型标量可以是整数或浮点数。Perl 会根据需要自动在它们之间进行转换。数字也可以用八进制(以 0 为前缀)或十六进制(以 0x 为前缀)指定。数字内部可以使用下划线来提高可读性(例如,1_000_000)。

#!/usr/bin/perl
use strict;
use warnings;
my $integer_val = 2023;
my $negative_val = -50;
my $float_val = 3.14159;
my $scientific_notation = 6.022e23; # 阿伏伽德罗常数
my $octal_val = 0377; # 十进制的 255 (3*64 + 7*8 + 7*1)
my $hex_val = 0xFF; # 十进制的 255 (15*16 + 15*1)
my $large_number = 1_234_567.89;
print "Integer: $integer_val\n";
print "Negative: $negative_val\n";
print "Float: $float_val\n";
print "Scientific: $scientific_notation\n";
print "Octal (0377) in decimal: $octal_val\n";
print "Hex (0xFF) in decimal: $hex_val\n";
print "Large Number: $large_number\n";

输出:

Integer: 2023
Negative: -50
Float: 3.14159
Scientific: 6.022e+23
Octal (0377) in decimal: 255
Hex (0xFF) in decimal: 255
Large Number: 1234567.89

字符串型标量是字符序列。它们可以使用单引号('...')或双引号("...")定义。

  • 单引号字符串: 几乎不做内插(interpolation)。只有 \'(用于嵌入单引号)和 \\(用于嵌入反斜杠)是特殊的。
  • 双引号字符串: 执行变量内插(例如,$variable 会被替换为其值)和转义序列处理(例如,\n 表示换行,\t 表示制表符)。
#!/usr/bin/perl
use strict;
use warnings;
my $fruit = "apple";
my $single_quoted = 'This is a $fruit. \n (literal)';
my $double_quoted = "This is an $fruit. \n (interpolated)";
my $escaped_chars = "Path: C:\\Program Files\\My App\nPrice: \$100";
print "Single: $single_quoted\n";
print "Double: $double_quoted"; # 注意:这里没有 \n,直接使用了字符串中的 \n
print "Escaped: $escaped_chars\n";

输出:

Single: This is a $fruit. \n (literal)
Double: This is an apple.
(interpolated)Escaped: Path: C:\Program Files\My App
Price: $100

Perl 为标量提供了各种运算符。数值运算符(+、-、*、/、%、**)用于数字。字符串连接使用点运算符(.)。字符串重复运算符是 x。

#!/usr/bin/perl
use strict;
use warnings;
my $greeting = "Hello" . " " . "Perl!"; # 字符串连接
my $sum = 10 + 5.5; # 数值相加(结果是 15.5)
my $product = 3 * 7; # 数值相乘
my $repeated_str = "Ha" x 3; # 字符串重复 ("HaHaHa")
my $mixed_concat = $greeting . $sum; # 数字被转换为字符串进行连接
print "Greeting: $greeting\n";
print "Sum: $sum\n";
print "Product: $product\n";
print "Repeated: $repeated_str\n";
print "Mixed: $mixed_concat\n";

输出:

Greeting: Hello Perl!
Sum: 15.5
Product: 21
Repeated: HaHaHa
Mixed: Hello Perl!15.5

Perl 会根据所使用的运算符自动在字符串和数字之间进行转换。这被称为自动激活(auto-vivification)或 DWIM(Do What I Mean,做我所指)行为。要检查标量是否看起来像一个数字,可以使用 Scalar::Util 模块中的 looks_like_number 函数。

对于较长的多行字符串,Here Documents 非常方便。它们以 << 开头,后跟一个分隔符标记,然后在后续行上是字符串内容,最后以该分隔符标记单独占一行结束。

#!/usr/bin/perl
use strict;
use warnings;
my $message_type = "Urgent";
my $email_body = <<"END_OF_EMAIL";
To: recipient@example.com
From: sender@example.com
Subject: $message_type Message
This is the body of the email.
It can span multiple lines.
Best regards,
The Sender
END_OF_EMAIL
print $email_body;
# 缩进的 here-doc (Perl 5.26+)
my $indented_text = <<~"STOP";
This line is indented.
So is this one.
STOP
print "---\n$indented_text---";

如果分隔符被引用(例如,<<"EOF"),其行为类似于双引号字符串(会发生内插)。如果未被引用(例如,<<EOF),其行为也类似于双引号。如果使用单引号(例如,<<'EOF'),则类似于单引号字符串(不发生内插)。在分隔符前使用 ~(Perl 5.26+)允许正文和结束标记缩进。

V-Strings(版本字符串 / 向量字符串)

Section titled “V-Strings(版本字符串 / 向量字符串)”

v1.2.3 或 v65.66.67 形式的字面量是一个 V-String。它被解析为一个由指定序数(点分隔的数字)对应的字符组成的字符串。如果 v 后面只有一个数字(例如 v65),它被解释为一个具有该序数值的单个字符。如果没有点且数字多于一个(例如 v102.111.111),这种语法主要用于表示版本号,但 Perl 可能会将 v1.2.3 解析为 \x1\x2\x3。

历史上,V-Strings 旨在用于版本号。通过序数构造任意字符字符串(例如,用 v65.66.67 表示 “ABC”)的方式不如使用 chr(65) . chr(66) . chr(67) 或 "\x41\x42\x43" 常见。

#!/usr/bin/perl
use strict;
use warnings;
use utf8; # 对于正确处理 Unicode 字符很重要
use feature 'say';
my $version_string = v5.36.0;
say "Version: $version_string"; # 通常字符串化后看起来像 "^X6\0"
# 要获取预期的版本表示,最好这样处理:
# printf "Version: %vd\n", $version_string; # 打印 5.36.0
my $char_A = v65;
say "Character A (from v65): $char_A"; # 输出: A
my $hello_world_ords = v72.101.108.108.111.32.87.111.114.108.100;
say "String from ordinals: $hello_world_ords"; # 输出: Hello World
# 现代方式打印版本对象:
my $perl_version = $^V; # $^V 是一个保存 Perl 版本为 v-string 的特殊变量
printf "Running Perl version: %vd\n", $perl_version; # 打印实际版本

输出(版本表示可能因未使用 printf %vd 而略有不同):

Version: \x05(\x00
Character A (from v65): A
String from ordinals: Hello World
Running Perl version: 5.xx.y (actual version)

Perl 提供了一些特殊字面量,代表程序当前的特定信息:

  • __FILE__:当前文件的名称。
  • __LINE__:脚本中的当前行号。
  • __PACKAGE__:当前包(命名空间)。
  • __SUB__:对当前子例程(subroutine)的引用(Perl 5.16+)。
  • __END__:在逻辑上结束脚本。__END__ 之后的任何文本都可以通过 DATA 文件句柄读取。

这些是标记(tokens),而不是变量,因此它们不能直接在双引号字符串中内插,需要进行连接。

#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
sub show_literals {
say "This code is in package: " . __PACKAGE__;
say "Running from file: " . __FILE__;
say "Currently at line: " . __LINE__;
if (defined &${__SUB__}) { # 检查 __SUB__ 是否可用且已定义
say "Inside subroutine: " . ((__SUB__)->() ? (__SUB__)->() : 'N/A');
}
}
show_literals();
say "End of main script logic.";
__END__
This is data after __END__.
It can be read via the DATA filehandle.

输出(文件名和行号会有所不同):

This code is in package: main
Running from file: ./your_script_name.pl
Currently at line: 9
Inside subroutine: main::show_literals
End of main script logic.