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 反向引用

廣告