字符串字面量

Perl中的字符串和Shell类似,可以使用双引号或单引号包围。它们的区别是:

  • 双引号:双引号包围的是字符串字面量,但允许在双引号内使用变量内插(Interpolate)、表达式内插、反斜线转义、反斜线字符序列
  • 单引号:单引号包围的字符串字面量不允许使用变量内插、表达式内插、反斜线序列,也禁止几乎所有的反斜线转义,只允许对单引号自身和反斜线自身进行反斜线转义

例如,下面是一些字符串字面量:

use v5.12;
use warnings;

my $name = "junma";
my $age = 23;

say "hello";   # hello,普通字符串
say 'hello';   # hello,普通字符串

# 双引号内可以使用反斜线转义,可以使用单引号
# 单引号内只能转义单引号和反斜线,可以使用双引号
say "hello\"world, hello'world";  # hello"world, hello'world
say 'hello\'world, hello"world';  # hello'world, hello"world
say "hello\\world";  # hello\world
say 'hello\\world';  # hello\world

# 双引号内可以变量内插,单引号内不能变量内插
say "hello $name";    # hello junma
say 'hello $name';    # hello $name

# 双引号内可以表达式内插,单引号内不能表达式内插
say "age: @{[$age+2]}";  # age: 25
say 'age: @{[$age+2]}';  # age: @{[$age+2]}

# 双引号内转义变量内插、转义表达式内插
say "hello \$name";      # hello $name
say "age: \@{[$age+2]}"; # age: @{[23+2]}

# 双引号内可以使用反斜线字符序列,单引号内不允许使用
say "hello\tworld";  # hello_TAB_world
say 'hello\tworld';  # hello\tworld

变量内插不仅可以内插标量标量,还可以内插数组变量,但不能内插hash变量。

my @arr = (11, 22, 33);
say "@arr";     # 输出:11 22 33
my %person = (name=>"junma", age=>23);
say "%person";  # 输出:%person,hash变量不内插

反斜线字符序列

Perl除了支持换行符\n、制表符\t等ASCII的特殊字符外,还支持几个具有特殊意义的反斜线序列。

\u 修改下一个字符为大写
\l 修改下一个字符小写 
\U 修改后面所有字符大写 
\L 修改后面所有字符小写 
\Q 使后面的所有字符都成为字面符号
\E 结束\U \L或\Q的效果

例如:

print "\uabc"; # 输出Abc
print "\Uabc"; # 输出ABC
print "ab\Ucxyz";   # 输出abCXYZ
print "ab\Ucx\Eyz"; # 输出abCXyz

这些反斜线序列有时候非常实用,特别是在使用函数修改数据不方便时。例如,在正则的s替换语法s///中,replacement部分可以使用这些反斜线字符序列。

# 将匹配的字符换成大写
my $name = "junmajinlong";
$name =~ s/([j-n])/\U\1\E/g;
say $name;    # 输出:JuNMaJiNLoNg

使用q()和qq()替代单双引号

如果一个字符串比较复杂,使用单双引号包围字符串可能会非常麻烦。

Perl中可以使用q实现单引号相同的引用功能,使用qq实现双引号相同的引用功能。

例如:

q(abc)            # 等价于'abc'
qq(abc)           # 等价于"abc"
qq(hello"world)   # 等价于"hello\"world"

注意上面q()qq()的括号,它们是引用的起始符和终止符,它们可以被替换为其他成对的符号:要么是前后相同的单个标点字符,要么是对称的括号(大括号、小括号、尖括号、中括号都可以)。

say q!abc!;
say q<abc>;
say qq{abc};

甚至,还可以使用数值、字母作为起始符和终止符,但要求将起始符和q或qq使用空白分隔开。

say qq 1def1;   # 等价于qq(def)
say qq adefa;   # 等价于qq(def)

注意,如果字符串中出现了起始符或终止符,则需要对其反斜线转义。

say qq{abc\}def};  # 转义终止符
say qq{abc\{def};  # 转义起始符
say qq ad\aefa;    # 转义起始符a,输出daef

但是,在起始符和终止符之间,可以嵌套成对的起始符号和终止符号。

say qq{ab{cd}e};   # ab{cd}e

bareword

虽然觉得很诡异,但不用任何引号包围的字符也被Perl当作字符串,这种字符串称为Bareword(裸字符串)。如果开启了warnings功能,使用Bareword时,perl会警告。

$s = abc;   ## bareword字符串abc
say $s;   # abc

注意,不要直接在say/print/printf的第一个参数处使用bareword,因为第一个bareword会被它们当作输出文件句柄。

# 将bareword字符串hello写入标准输出
# 这里的STDOUT也是bareword,但会被say解析为输出文件句柄
say STDOUT hello;

不建议使用bareword。

v-str

Perl还支持一种称为v字符串的字面量,v字符串多用来表示版本号。例如use v5.12

v字符串有时候也能用在其他方面,例如保存点分十进制的IP地址,这里不深究v字符串,除了用在版本号上,多数时候也用不到它。

here doc

所谓heredoc,即表示此处内容是文档,即将文档内容当作字符串来处理。

既然是文档,就需要有文档起始符和文档结束符,分别标识文档从哪里起始,到哪里结束。一般来说,所有支持heredoc的语言,文档起始符和文档结束符必须相同(一般使用EOF或eof作为起始符和结束符),且结束符必须单独占行且顶格书写。

Perl中支持的heredoc格式如下,以print为例:

print <<EOF;
  line1
  line2
  line3
EOF

# 输出结果:
#  line1
#  line2
#  line3

这里以EOF作为文档起始符和结束符,起始符EOF后面加上分号结尾表示print语句结束。结束符EOF单独占用一行,且顶格书写。起始符和结束符中间是怎样的数据,输出时就是怎样的数据。

只要需要字符串的地方,都可以使用heredoc来表示这部分字符串。例如,将heredoc字符串赋值给变量、作为函数字符串实参,等等。

# heredoc作为字符串赋值给变量
$msg = <<EOF;
  HELLO
  WORLD
EOF
print $msg;

可以为heredoc的起始符加上单引号、双引号以及反引号。它们的效果和普通的单、双、反引号的效果一样:

  • 单引号内只允许\\ \'这两种转义
  • 双引号内允许变量内插、表达式内插、反斜线转义、反斜线字符序列
  • 加反引号` `,表示将字符串放进Shell环境执行(和Shell的命令替换效果差不多)
  • 不加引号等价于加双引号,加反斜线前缀\EOF等价于加单引号

加单双引号:

$name="malongshuai";
print <<'EOF';
  haha
  \$name # 反斜线转义功能失效
  
  $name # 变量无法替换
EOF

print <<"EOF";
  haha
  \$name # 反斜线成功转义
  
  $name # 变量成功替换
EOF

加反引号:

print <<`EOF`;
  date +"%F %T"
EOF

另外,从Perl v5.26开始,可以在heredoc的起始符EOF或被引用的EOF前加上波浪号

  • <<~EOF <<~"EOF"
  • <<~'EOF' <<~\EOF
  • <<~`EOF`

加上前缀波浪号,使得heredoc允许终止符被缩进,且会将起始符和终止符之间的heredoc内容的空白前缀进行修剪,使之与终止符的缩进进行对齐。看示例理解。

print <<~EOF;
  line1
    line2
  line3
  EOF
  
print <<~\eof;
    LINE1
  LINE2
    LINE3
  eof

输出:

line1
  line2
line3
  LINE1
LINE2  
  LINE3

使用波浪号前缀时,要求heredoc的内容必须不能出现在终止符之前,否则报错。例如下面代码会报错。

print <<~\eof;
    LINE1
LINE2
    LINE3
  eof

可以同时在一个语句中使用多个heredoc,它们互不影响。例如:

print <<~EOF, <<~\eof;
  line1
    line2
  line3
  EOF
    LINE1
      LINE2
    LINE3
    eof

输出:

line1  
  line2
line3  
LINE1  
  LINE2
LINE3 

由于heredoc的内容带有尾部换行符,如果想去掉这个尾部换行符,可:

chomp(my $str = <<STR);
line1
line2
STR

print $str;

字符串串联和重复

Perl使用点.来串联字符串。

例如:

my $name = "junmajinlong";
say "www.".$name.".com";  # 输出:www.junmajinlong.com

Perl使用x(字母xyz的x)来重复字符串指定次数:

say '-' x 5;   # 重复5次,输出:-----

需注意,如果x重复次数是小数,则截断为整数,如果x是0,则清空字符串。

say '-' x 5.6;  # 输出-----
say '-' x 0;    # 输出空字符串

数值和字符串的类型自动转换

在前一面介绍数值字面量的小节中曾提到过,当使用运算符+ - * / % -(负号) ++ -- abs以及< <= > >= == !=时,都会将操作数强制转换为数值。

对于字符串来说,当使用.串联字符串或使用x来重复字符串时,会将操作对象强制转换为字符串。

# 字符串转换为数值
"0333" + 22  # 返回355
"12abc" * 3  # 36
"abc12" * 4  # 0
" 12abc" * 3 # 36

# 数值转换为字符串
"033".22    # 返回03322
033.22      # 返回2722,033表示8进制,转换为十进制为27(3*8+3)

注意,当字符串操作符和数值操作符混用时,注意它们的优先级。如果不确定优先级,使用括号强制改变优先级:

"abc".5*3 # 返回abc15,乘法先运算
"abc".5 + 3 # 返回3,"."先运算
"abc".(5+3) # 返回abc8