Perl - 正则表达式



正则表达式是一串字符,用于定义您正在查看的模式或模式。Perl 中正则表达式的语法与您在其他支持正则表达式的程序(如sedgrepawk)中找到的非常相似。

应用正则表达式的基本方法是使用模式绑定运算符 =~ 和!~。第一个运算符是测试和赋值运算符。

Perl 中有三个正则表达式运算符。

  • 匹配正则表达式 - m//
  • 替换正则表达式 - s///
  • 转录正则表达式 - tr///

在每种情况下,正斜杠都充当您指定的正则表达式 (regex) 的分隔符。如果您习惯使用任何其他分隔符,则可以使用它代替正斜杠。

匹配运算符

匹配运算符 m// 用于将字符串或语句与正则表达式匹配。例如,要将字符序列“foo”与标量 $bar 匹配,您可以使用以下语句:

#!/usr/bin/perl

$bar = "This is foo and again foo";
if ($bar =~ /foo/) {
   print "First time is matching\n";
} else {
   print "First time is not matching\n";
}

$bar = "foo";
if ($bar =~ /foo/) {
   print "Second time is matching\n";
} else {
   print "Second time is not matching\n";
}

执行上述程序时,会产生以下结果:

First time is matching
Second time is matching

m// 实际上以与 q// 运算符系列相同的方式工作。您可以使用任何自然匹配字符的组合来充当表达式的分隔符。例如,m{}、m() 和 m>< 都是有效的。因此,上述示例可以改写如下:

#!/usr/bin/perl

$bar = "This is foo and again foo";
if ($bar =~ m[foo]) {
   print "First time is matching\n";
} else {
   print "First time is not matching\n";
}

$bar = "foo";
if ($bar =~ m{foo}) {
   print "Second time is matching\n";
} else {
   print "Second time is not matching\n";
}

如果分隔符是正斜杠,则可以省略 m,但对于所有其他分隔符,您必须使用 m 前缀。

请注意,整个匹配表达式(即 =~ 或 !~ 左侧的表达式和匹配运算符)如果表达式匹配,则在标量上下文中返回 true。因此,语句:

$true = ($foo =~ m/foo/);

如果 $foo 与正则表达式匹配,则将 $true 设置为 1,否则如果匹配失败则设置为 0。在列表上下文中,匹配返回任何分组表达式的内容。例如,在从时间字符串中提取小时、分钟和秒时,我们可以使用:

my ($hours, $minutes, $seconds) = ($time =~ m/(\d+):(\d+):(\d+)/);

匹配运算符修饰符

匹配运算符支持自己的修饰符集。/g 修饰符允许全局匹配。/i 修饰符将使匹配不区分大小写。以下是修饰符的完整列表

序号 修饰符和描述
1

i

使匹配不区分大小写。

2

m

指定如果字符串具有换行符或回车符,则 ^ 和 $ 运算符将匹配换行符边界,而不是字符串边界。

3

o

仅评估表达式一次。

4

s

允许使用 . 匹配换行符。

5

x

允许您在表达式中使用空格以提高清晰度。

6

g

全局查找所有匹配项。

7

cg

允许搜索即使在全局匹配失败后继续。

仅匹配一次

匹配运算符还有一个更简单的版本 - ?PATTERN? 运算符。这基本上与 m// 运算符相同,只是它在每次重置之间的每次调用中只匹配字符串中的一次。

例如,您可以使用它来获取列表中的第一个和最后一个元素:

#!/usr/bin/perl

@list = qw/food foosball subeo footnote terfoot canic footbrdige/;

foreach (@list) {
   $first = $1 if /(foo.*?)/;
   $last = $1 if /(foo.*)/;
}
print "First: $first, Last: $last\n";

执行上述程序时,会产生以下结果:

First: foo, Last: footbrdige

正则表达式变量

正则表达式变量包括$,它包含上次分组匹配匹配的内容;$&,它包含整个匹配字符串;$`,它包含匹配字符串之前的所有内容;以及$',它包含匹配字符串之后的所有内容。以下代码演示了结果:

#!/usr/bin/perl

$string = "The food is in the salad bar";
$string =~ m/foo/;
print "Before: $`\n";
print "Matched: $&\n";
print "After: $'\n";

执行上述程序时,会产生以下结果:

Before: The
Matched: foo
After: d is in the salad bar

替换运算符

替换运算符 s/// 实际上只是匹配运算符的扩展,它允许您将匹配的文本替换为一些新文本。运算符的基本形式为:

s/PATTERN/REPLACEMENT/;

PATTERN 是我们要查找的文本的正则表达式。REPLACEMENT 是我们要用来替换找到的文本的文本或正则表达式的规范。例如,我们可以使用以下正则表达式将所有出现的dog替换为cat

#/user/bin/perl

$string = "The cat sat on the mat";
$string =~ s/cat/dog/;

print "$string\n";

执行上述程序时,会产生以下结果:

The dog sat on the mat

替换运算符修饰符

以下是与替换运算符一起使用的所有修饰符的列表。

序号 修饰符和描述
1

i

使匹配不区分大小写。

2

m

指定如果字符串具有换行符或回车符,则 ^ 和 $ 运算符将匹配换行符边界,而不是字符串边界。

3

o

仅评估表达式一次。

4

s

允许使用 . 匹配换行符。

5

x

允许您在表达式中使用空格以提高清晰度。

6

g

将所有找到的表达式的出现替换为替换文本。

7

e

将替换评估为 Perl 语句,并将其返回值用作替换文本。

转换运算符

转换类似于但并不完全相同于替换的原理,但与替换不同,转换(或转录)不使用正则表达式进行其搜索和替换值。转换运算符为:

tr/SEARCHLIST/REPLACEMENTLIST/cds
y/SEARCHLIST/REPLACEMENTLIST/cds

转换将 SEARCHLIST 中所有出现的字符替换为 REPLACEMENTLIST 中相应的字符。例如,使用我们在本章中一直在使用的“The cat sat on the mat.” 字符串:

#/user/bin/perl

$string = 'The cat sat on the mat';
$string =~ tr/a/o/;

print "$string\n";

执行上述程序时,会产生以下结果:

The cot sot on the mot.

也可以使用标准的 Perl 范围,允许您通过字母或数值指定字符范围。要更改字符串的大小写,您可以使用以下语法代替uc函数。

$string =~ tr/a-z/A-Z/;

转换运算符修饰符

以下是与转换相关的运算符列表。

序号 修饰符和描述
1

c

补充 SEARCHLIST。

2

d

删除找到但未替换的字符。

3

s

压缩重复替换的字符。

/d 修饰符删除与 SEARCHLIST 匹配但 REPLACEMENTLIST 中没有相应条目的字符。例如:

#!/usr/bin/perl 

$string = 'the cat sat on the mat.';
$string =~ tr/a-z/b/d;

print "$string\n";

执行上述程序时,会产生以下结果:

b b   b.

最后一个修饰符 /s 删除了被替换的字符的重复序列,因此:

#!/usr/bin/perl

$string = 'food';
$string = 'food';
$string =~ tr/a-z/a-z/s;

print "$string\n";

执行上述程序时,会产生以下结果:

fod

更复杂的正则表达式

您不仅可以匹配固定字符串。实际上,您可以使用更复杂的正则表达式匹配几乎任何您能想到的东西。这是一个快速备忘单:

下表列出了 Python 中可用的正则表达式语法。

序号 模式和描述
1

^

匹配行首。

2

$

匹配行尾。

3

.

匹配除换行符之外的任何单个字符。使用 m 选项允许它匹配换行符。

4

[...]

匹配括号中的任何单个字符。

5

[^...]

匹配括号中不存在的任何单个字符。

6

*

匹配前一个表达式的 0 次或多次出现。

7

+

匹配前一个表达式的 1 次或多次出现。

8

?

匹配前一个表达式的 0 次或 1 次出现。

9

{ n}

匹配前一个表达式的正好 n 次出现。

10

{ n,}

匹配前一个表达式的 n 次或多次出现。

11

{ n, m}

匹配前一个表达式的至少 n 次且至多 m 次出现。

12

a| b

匹配 a 或 b。

13

\w

匹配单词字符。

14

\W

匹配非单词字符。

15

\s

匹配空格。相当于 [\t\n\r\f]。

16

\S

匹配非空格。

17

\d

匹配数字。相当于 [0-9]。

18

\D

匹配非数字。

19

\A

匹配字符串开头。

20

\Z

匹配字符串结尾。如果存在换行符,则匹配换行符之前。

21

\z

匹配字符串结尾。

22

\G

匹配上次匹配完成的位置。

23

\b

在括号外匹配单词边界。在括号内匹配退格键 (0x08)。

24

\B

匹配非单词边界。

25

\n, \t, 等。

匹配换行符、回车符、制表符等。

26

\1...\9

匹配第 n 个分组子表达式。

27

\10

如果第 n 个分组子表达式已匹配,则匹配它。否则引用字符代码的八进制表示形式。

28

[aeiou]

匹配给定集合中的单个字符

29

[^aeiou]

匹配给定集合之外的单个字符

^ 元字符匹配字符串的开头,而 $ 元字符匹配字符串的结尾。以下是一些简短的示例。

# nothing in the string (start and end are adjacent)
/^$/   

# a three digits, each followed by a whitespace
# character (eg "3 4 5 ")
/(\d\s) {3}/  

# matches a string in which every
# odd-numbered letter is a (eg "abacadaf")
/(a.)+/  

# string starts with one or more digits
/^\d+/

# string that ends with one or more digits
/\d+$/

让我们看另一个例子。

#!/usr/bin/perl

$string = "Cats go Catatonic\nWhen given Catnip";
($start) = ($string =~ /\A(.*?) /);
@lines = $string =~ /^(.*?) /gm;
print "First word: $start\n","Line starts: @lines\n";

执行上述程序时,会产生以下结果:

First word: Cats
Line starts: Cats When

匹配边界

\b 在任何单词边界处匹配,由 \w 类和 \W 类之间的差异定义。因为 \w 包括单词的字符,而 \W 包括相反的字符,所以这通常意味着单词的终止。\B 断言匹配任何不是单词边界的位置。例如:

/\bcat\b/ # Matches 'the cat sat' but not 'cat on the mat'
/\Bcat\B/ # Matches 'verification' but not 'the cat on the mat'
/\bcat\B/ # Matches 'catatonic' but not 'polecat'
/\Bcat\b/ # Matches 'polecat' but not 'catatonic'

选择备选方案

| 字符就像 Perl 中的标准或按位 OR。它在正则表达式或组中指定备选匹配。例如,要在表达式中匹配“cat”或“dog”,您可以使用:

if ($string =~ /cat|dog/)

您可以将表达式的各个元素组合在一起以支持复杂的匹配。搜索两个人的姓名可以通过两个单独的测试来实现,如下所示:

if (($string =~ /Martin Brown/) ||  ($string =~ /Sharon Brown/))

This could be written as follows

if ($string =~ /(Martin|Sharon) Brown/)

分组匹配

从正则表达式的角度来看,两者之间没有区别,也许除了前者稍微更清晰之外。

$string =~ /(\S+)\s+(\S+)/;

and 

$string =~ /\S+\s+\S+/;

但是,分组的好处是它允许我们从正则表达式中提取序列。分组按其在原始表达式中出现的顺序返回为列表。例如,在以下片段中,我们从字符串中提取了小时、分钟和秒。

my ($hours, $minutes, $seconds) = ($time =~ m/(\d+):(\d+):(\d+)/);

除了这种直接方法之外,匹配的组也存在于特殊的 $x 变量中,其中 x 是正则表达式中组的编号。因此,我们可以将前面的示例改写如下:

#!/usr/bin/perl

$time = "12:05:30";

$time =~ m/(\d+):(\d+):(\d+)/;
my ($hours, $minutes, $seconds) = ($1, $2, $3);

print "Hours : $hours, Minutes: $minutes, Second: $seconds\n";

执行上述程序时,会产生以下结果:

Hours : 12, Minutes: 05, Second: 30

当组用于替换表达式时,$x 语法可以在替换文本中使用。因此,我们可以使用以下方法重新格式化日期字符串:

#!/usr/bin/perl

$date = '03/26/1999';
$date =~ s#(\d+)/(\d+)/(\d+)#$3/$1/$2#;

print "$date\n";

执行上述程序时,会产生以下结果:

1999/03/26

\G 断言

\G 断言允许您从上次匹配发生的位置继续搜索。例如,在以下代码中,我们使用了 \G,以便我们可以搜索到正确的位置,然后提取一些信息,而无需创建更复杂、单个的正则表达式:

#!/usr/bin/perl

$string = "The time is: 12:31:02 on 4/12/00";

$string =~ /:\s+/g;
($time) = ($string =~ /\G(\d+:\d+:\d+)/);
$string =~ /.+\s+/g;
($date) = ($string =~ m{\G(\d+/\d+/\d+)});

print "Time: $time, Date: $date\n";

执行上述程序时,会产生以下结果:

Time: 12:31:02, Date: 4/12/00

\G 断言实际上只是 pos 函数的元字符等效项,因此在正则表达式调用之间,您可以继续使用 pos,甚至可以通过将 pos 用作 lvalue 子程序来修改 pos 的值(以及 \G)。

正则表达式示例

文字字符

序号 示例和描述
1

Perl

匹配“Perl”。

字符类

序号 示例和描述
1

[Pp]ython

匹配“Python”或“python”

2

rub[ye]

匹配 "ruby" 或 "rube"

3

[aeiou]

匹配任何一个小写元音

4

[0-9]

匹配任何数字;等同于 [0123456789]

5

[a-z]

匹配任何小写 ASCII 字母

6

[A-Z]

匹配任何大写 ASCII 字母

7

[a-zA-Z0-9]

匹配以上任何一个

8

[^aeiou]

匹配除了小写元音之外的任何字符

9

[^0-9]

匹配除了数字之外的任何字符

特殊字符类

序号 示例和描述
1

.

匹配除了换行符之外的任何字符

2

\d

匹配一个数字: [0-9]

3

\D

匹配一个非数字: [^0-9]

4

\s

匹配一个空白字符: [ \t\r\n\f]

5

\S

匹配非空白字符: [^ \t\r\n\f]

6

\w

匹配单个单词字符: [A-Za-z0-9_]

7

\W

匹配非单词字符: [^A-Za-z0-9_]

重复情况

序号 示例和描述
1

ruby?

匹配 "rub" 或 "ruby": y 是可选的

2

ruby*

匹配 "rub" 加 0 个或多个 y

3

ruby+

匹配 "rub" 加 1 个或多个 y

4

\d{3}

精确匹配 3 个数字

5

\d{3,}

匹配 3 个或更多个数字

6.

\d{3,5}

匹配 3、4 或 5 个数字

非贪婪重复

这将匹配最少的重复次数 -

序号 示例和描述
1

<.*>

贪婪重复:匹配 "<python>perl>"

2

<.*?>

非贪婪:在 "<python>perl>" 中匹配 "<python>"

使用括号进行分组

序号 示例和描述
1

\D\d+

无分组:+ 重复 \d

2

(\D\d)+

分组:+ 重复 \D\d 对

3

([Pp]ython(, )?)+

匹配 "Python"、"Python, python, python" 等。

反向引用

这将再次匹配先前匹配的组 -

序号 示例和描述
1

([Pp])ython&\1ails

匹配 python&pails 或 Python&Pails

2

(['"])[^\1]*\1

单引号或双引号字符串。\1 匹配第一个组匹配到的任何内容。\2 匹配第二个组匹配到的任何内容,依此类推。

备选方案

序号 示例和描述
1

python|perl

匹配 "python" 或 "perl"

2

rub(y|le))

匹配 "ruby" 或 "ruble"

3

Python(!+|\?)

"Python" 后跟一个或多个 ! 或一个 ?

锚点

这需要指定匹配位置。

序号 示例和描述
1

^Python

匹配字符串或内部行开头的 "Python"

2

Python$

匹配字符串或行结尾的 "Python"

3

\APython

匹配字符串开头的 "Python"

4

Python\Z

匹配字符串结尾的 "Python"

5

\bPython\b

匹配单词边界处的 "Python"

6

\brub\B

\B 是非单词边界:在 "rube" 和 "ruby" 中匹配 "rub",但不能单独匹配

7

Python(?=!)

匹配 "Python",如果后面跟着一个感叹号

8

Python(?!!)

匹配 "Python",如果后面不跟着感叹号

带括号的特殊语法

序号 示例和描述
1

R(?#comment)

匹配 "R"。其余部分是注释

2

R(?i)uby

在匹配 "uby" 时不区分大小写

3

R(?i:uby)

与上面相同

4

rub(?:y|le))

仅分组,不创建 \1 反向引用

广告