强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

第 5 章:运算符

第 5 章:运算符

“运算符是表达式的骨架”

Perl 拥有极其丰富的运算符系统,尤其擅长字符串操作和正则表达式。


5.1 算术运算符

运算符说明示例结果
+加法3 + 25
-减法3 - 21
*乘法3 * 26
/除法7 / 23.5
%取模7 % 21
**幂运算2 ** 101024
++自增$x++先返回再加
--自减--$x先减再返回
use strict;
use warnings;

my $a = 10;
my $b = 3;

printf "%d + %d = %d\n", $a, $b, $a + $b;
printf "%d - %d = %d\n", $a, $b, $a - $b;
printf "%d * %d = %d\n", $a, $b, $a * $b;
printf "%d / %d = %.2f\n", $a, $b, $a / $b;
printf "%d %% %d = %d\n", $a, $b, $a % $b;
printf "%d ** %d = %d\n", 2, 10, 2 ** 10;

# 自增自减
my $x = 5;
print $x++, "\n";   # 输出 5,然后 $x 变为 6
print $x, "\n";     # 输出 6
print ++$x, "\n";   # $x 变为 7,输出 7

5.2 字符串运算符

运算符说明示例结果
.字符串拼接"Hello" . " " . "World""Hello World"
x字符串重复"ab" x 3"ababab"
ne字符串不等于"abc" ne "def"1 (真)
eq字符串等于"abc" eq "abc"1 (真)
lt字符串小于"abc" lt "def"1 (真)
gt字符串大于"def" gt "abc"1 (真)
le字符串小于等于"abc" le "abc"1 (真)
ge字符串大于等于"def" ge "abc"1 (真)
cmp字符串比较"abc" cmp "def"-1
# 拼接
my $greeting = "Hello" . " " . "World" . "!";
print "$greeting\n";   # Hello World!

# 重复
my $line = "-" x 40;
print "$line\n";        # ----------------------------------------

# 字符串比较
my $a = "apple";
my $b = "banana";

if ($a lt $b) {
    print "$a 在 $b 前面(字典序)\n";
}

# cmp 三路比较
my $result = $a cmp $b;
print "比较结果: $result\n";   # -1($a < $b)

字符串比较 vs 数值比较

# 这是 Perl 新手最容易犯的错误!
my $x = "02";
my $y = "10";

# 字符串比较(字典序)
print "字符串: $x eq $y ? ", ($x eq $y ? "是" : "否"), "\n";  # 否
print "字符串: $x gt $y ? ", ($x gt $y ? "是" : "否"), "\n";  # 是('0' > '1' 错误,实际 '02' gt '10' 因为 '0' < '1')

# 数值比较
print "数值: $x == $y ? ", ($x == $y ? "是" : "否"), "\n";    # 否
print "数值: $x > $y ? ", ($x > $y ? "是" : "否"), "\n";      # 否

# 正确理解:
# "02" lt "10" 因为 '0' 的 ASCII 码 (48) < '1' 的 ASCII 码 (49)
# 2 < 10 数值上
比较类型运算符示例
数值相等==$a == $b
数值不等!=$a != $b
数值大小<, >, <=, >=$a > $b
数值三路<=>$a <=> $b
字符串相等eq$a eq $b
字符串不等ne$a ne $b
字符串大小lt, gt, le, ge$a gt $b
字符串三路cmp$a cmp $b

5.3 逻辑运算符

传统风格

运算符说明短路
&&逻辑与✅ 左侧为假则不计算右侧
||逻辑或✅ 左侧为真则不计算右侧
!逻辑非-

词组风格(推荐)

运算符说明短路
and逻辑与
or逻辑或
not逻辑非-
# 传统风格(优先级高)
my $x = $a && $b;         # $a 为真则返回 $b,否则返回 $a
my $y = $a || $b;         # $a 为真则返回 $a,否则返回 $b

# 词组风格(优先级低,适合流程控制)
open my $fh, '<', 'file.txt' or die "无法打开: $!\n";
send_email($to, $body) and log("邮件已发送");

逻辑运算符的返回值

# && 返回第一个假值或最后一个值
print 0 && "hello";       # 0
print 1 && "hello";       # "hello"
print "a" && "b" && "c";  # "c"

# || 返回第一个真值或最后一个值
print 0 || "hello";       # "hello"
print "a" || "b";         # "a"
print 0 || "" || undef;   # undef

# // (Perl 5.10+) - 定义或运算符
# 返回第一个已定义的值
my $name = $input // "默认值";   # $input 未定义则用默认值

5.4 位运算符

运算符说明示例
&按位与0b1010 & 0b11000b1000
|按位或0b1010 | 0b11000b1110
^按位异或0b1010 ^ 0b11000b0110
~按位取反~0b1010 → 取反结果
<<左移1 << 3 → 8
>>右移16 >> 2 → 4
my $perms = 0755;
printf "权限: %04o\n", $perms;      # 0755
printf "所有者: %03o\n", ($perms >> 6) & 07;
printf "组: %03o\n", ($perms >> 3) & 07;
printf "其他: %03o\n", $perms & 07;

5.5 赋值运算符

运算符等价于说明
$x += 5$x = $x + 5加法赋值
$x -= 5$x = $x - 5减法赋值
$x *= 2$x = $x * 2乘法赋值
$x /= 2$x = $x / 2除法赋值
$x .= "str"$x = $x . "str"字符串拼接赋值
$x x= 3$x = $x x 3字符串重复赋值
$x //= "val"$x = $x // "val"定义或赋值
$x &&= "val"$x = $x && "val"与赋值
$x ||= "val"$x = $x || "val"或赋值
# 默认值设置
my $name = $ENV{USER} // "anonymous";
my $count ||= 0;   # 如果未定义则设为 0

# 字符串构建
my $html = "";
$html .= "<h1>标题</h1>\n";
$html .= "<p>内容</p>\n";

5.6 范围运算符

数字范围

my @nums = 1..10;            # (1, 2, 3, ..., 10)
my @reverse = 10..1;         # () 空!范围必须从小到大
my @letters = 'a'..'z';      # (a, b, c, ..., z)

范围运算符在布尔上下文中

在布尔上下文中,.. 成为 Flip-Flop 运算符

# 打印第 3 到第 7 行
while (<STDIN>) {
    print if 3..7;
}

# 打印从 START 到 END 之间的内容
while (<STDIN>) {
    print if /^START/ .. /^END/;
}

5.7 三元运算符

my $age = 20;
my $status = ($age >= 18) ? "成年人" : "未成年人";
print "$status\n";

# 嵌套三元(不推荐过度嵌套)
my $level = ($score >= 90) ? "优秀"
          : ($score >= 80) ? "良好"
          : ($score >= 60) ? "及格"
          :                  "不及格";

5.8 正则绑定运算符

正则表达式的详细内容在第 8 章,这里先介绍基本运算符:

运算符说明示例
=~绑定匹配$str =~ /pattern/
!~绑定不匹配`$str !~ /pattern/
m//匹配m/pattern/
s///替换s/old/new/
qr//引用正则my $re = qr/pattern/
my $email = "[email protected]";

# 匹配
if ($email =~ /^[\w.]+@[\w.]+\.\w+$/) {
    print "有效的邮箱地址\n";
}

# 不匹配
if ($email !~ /spam/i) {
    print "不是垃圾邮件\n";
}

# 替换
(my $redacted = $email) =~ s/^(.{3}).*/$1***@***/;
print "$redacted\n";   # use***@***

5.9 逗号运算符与胖箭头

# 逗号 , 是列表运算符
my @list = (1, 2, 3);

# 胖箭头 => 自动为左侧加引号
my %hash = (
    name  => "Perl",       # 等价于 "name" => "Perl"
    year  => 1987,
    motto => "TMTOWTDI",
);

5.10 运算符优先级

从高到低:

优先级运算符说明
最高() [] {}括号、下标、块
**
! ~ \ ++ --一元运算符
=~ !~正则绑定
* / % x乘法类
+ - .加法类
<< >>移位
命名运算符-X 文件测试、print
< > <= >= lt gt le ge比较
== != <=> eq ne cmp相等
&按位与
| ^按位或
&&逻辑与
|| //逻辑或
.. ...范围
?:三元
= += -=赋值
最低, =>逗号
not逻辑非
and逻辑与
or xor逻辑或

最佳实践:不确定优先级时,使用括号!

# 不清晰
my $result = $a + $b * $c;

# 清晰明了
my $result = $a + ($b * $c);

5.11 业务场景:模板引擎

#!/usr/bin/env perl
use strict;
use warnings;

# 简单的模板变量替换引擎
sub render_template {
    my ($template, %vars) = @_;

    # 替换 {{variable}} 占位符
    $template =~ s/\{\{(\w+)\}\}/$vars{$1} // "{{$1}}"/ge;

    # 替换 {{if condition}}...{{/if}}
    $template =~ s/\{\{if (\w+)\}\}(.*?)\{\{\/if\}\}/
        $vars{$1} ? $2 : ""/ges;

    return $template;
}

my $tpl = <<'TEMPLATE';
<h1>{{title}}</h1>
<p>作者: {{author}}</p>
{{if published}}<span class="badge">已发布</span>{{/if}}
TEMPLATE

my %data = (
    title     => "Perl 入门教程",
    author    => "张三",
    published => 1,
);

print render_template($tpl, %data);

本章小结

要点内容
数值 vs 字符串比较== vs eq,千万不能混用
逻辑运算符`
//定义或运算符(Perl 5.10+)
x字符串重复运算符
..范围运算符 / Flip-Flop
优先级不确定时加括号

练习

  1. 编写代码验证 "10" eq "10""10" == "10" 的结果
  2. 使用 x 运算符生成一个 40 个字符宽的分隔线
  3. 编写代码演示 ||// 的区别(使用 undef0""
  4. 使用范围运算符生成字母表并打印
  5. 实现一个简单的计算器,支持 + - * / % **

扩展阅读