天天看點

一文掌握字元串之正規表達式,值得收藏!

前言:

授人以魚不如授人以漁,大家在程式設計的時候總會遇到要查找某些複雜規則的字元串,例如在 linux 系統中,需要對多個檔案裡的某段代碼進行替換,你是不是還在每個檔案打開逐一目标替換?如果你也有這樣的困惑那麼正規表達式就是你必須會的技能。

程式員必備資源,值得收藏!點選下載下傳

1、什麼是正規表達式

正規表達式是對字元串操作的一種邏輯公式,就是用事先定義好的一些特定字元、及這些特定字元的組合,組成一個 “規則字元串” ,這個 “規則字元串” 用來表達對字元串的一種過濾邏輯。換句話說,正規表達式就是記錄文本規則的代碼。

很可能你使用過 Windows 下用于檔案查找的通配符(wildcard),也就是 * 和 ?。如果你想查找某個目錄下的所有的 pdf 文檔的話,可以直接搜尋 *.pdf,如下:

一文掌握字元串之正規表達式,值得收藏!

在這裡,* 會被解釋成任意的字元串。和通配符類似,正規表達式也是用來進行文本比對的工具,隻不過比起通配符,它能更精确地描述你的需求。當然,代價就是更複雜,比如你可以編寫一個正規表達式,用來查找所有以 0 開頭,後面跟着 2-3 個數字,然後是一個連字号 “-” ,最後是 7 或 8 位數字的字元串(像 011-12345678 或 0856-7654321)。

程式員相關的海量資料,點選免費擷取

2、入門

學習正規表達式的最好方法是從例子開始。

假如你在一篇英文期刊裡查找 me,你可以使用正規表達式 me。

這幾乎是最簡單的正規表達式了,它可以精确比對這樣的字元串:由兩個字元組成,前一個字元是 m, 後一個是 e。通常,處理正規表達式的工具會提供一個忽略大小寫的選項,如果選中了這個選項,它可以比對 me, ME, Me, mE 這四種情況中的任意一種。

不幸的是,很多單詞裡包含 hi 這兩個連續的字元,比如 me, mean, measure等等。用 me 來查找的話,這裡邊的 me 也會被找出來。如果要精确地查找 me 這個單詞的話,我們應該使用 \bme\b。

\b 是正規表達式規定的一個特殊代碼(有些人叫它元字元,metacharacter),代表着單詞的開頭或結尾,也就是單詞的分界處。雖然通常英文的單詞是由空格,标點符号或者換行來分隔的,但是 \b 并不比對這些單詞分隔字元中的任何一個,它隻比對一個位置。

假如你要找的是 me 後面不遠處跟着一個 james,你應該用 \bme\b.*\bjames\b。

這裡 . 是另一個元字元,比對除了換行符以外的任意字元。* 同樣是元字元,不過它代表的不是字元,也不是位置,而是數量——它指定 * 前邊的内容可以連續重複使用任意次以使整個表達式得到比對。

是以 .* 連在一起就意味着任意數量的不包含換行的字元。現在\bme\b.*\bjames\b的意思就很明顯了:先是一個單詞 me 然後是任意個任意字元(但不能是換行),最後是 james 這個單詞。

3、元字元

正規表達式由一些普通字元和一些元字元(metacharacters)組成。普通字元包括大小寫的字母和數字,而元字元則具有特殊的含義,要想真正的用好正規表達式,正确的了解元字元是最重要的事情。下表列出了常用的元字元

元字元 描述

. 比對除 “\n” 和 "\r" 之外的任何單個字元。要比對包括 “\n” 和 "\r" 在内的任何字元,請使用像 “[\s\S]” 的模式

\w 比對包括下劃線的任何單詞字元。類似但不等價于 “[A-Za-z0-9_]” ,這裡的 "單詞" 字元使用 Unicode 字元集

\s 比對任何不可見字元,包括空格、制表符、換頁符等等。等價于[ \f\n\r\t\v]

\d 比對一個數字字元。等價于 [0-9] 。grep 要加上 -P, perl 正則支援

\b 比對一個單詞的邊界,也就是指單詞和空格間的位置(即正規表達式的 “比對” 有兩種概念,一種是比對字元,一種是比對位置,這裡的 \b 就是比對位置的)。例如, “er\b” 可以比對 “never” 中的 “er” ,但不能比對 “verb” 中的 “er”; “\b1_” 可以比對 “1_23” 中的 “1_”,但不能比對 “21_3” 中的 “1_”

^ 比對輸入字行首。如果設定了 RegExp 對象的 Multiline 屬性, ^ 也比對 “\n” 或 “\r” 之後的位置。

$ 比對輸入行尾。如果設定了 RegExp 對象的 Multiline 屬性,$ 也比對 “\n” 或 “\r” 之前的位置。

4、字元轉義

如果想查找元字元本身的話,比如查找 . ,或者 * ,就出現了問題:你沒辦法指定它們,因為它們會被解釋成别的意思。這時就得使用 \ 來取消這些字元的特殊意義。是以,應該使用 . 和 *。當然,要查找 \ 本身,也得用 \。

例如:mayday\.net 比對 mayday.net ,C:\\Windows比對C:\Windows。

5、重複

已經看過了前面的 * , + 幾個比對重複的方式了。下面是正規表達式中所有的限定符(指定數量的代碼:

* 比對前面的子表達式任意次。例如,zo* 能比對 “z” ,也能比對 “zo” 以及 “zoo” 。* 等價于{0,}。

+ 比對前面的子表達式一次或多次(大于等于1次)。例如,“zo+” 能比對 “zo” 以及 “zoo”,但不能比對 “z” 。+ 等價于 {1,}。

? 比對前面的子表達式零次或一次。例如,“do(es)?” 可以比對 “do” 或 “does” 。 ? 等價于 {0,1}。

{n} n 是一個非負整數。比對确定的 n 次。例如, “o{2}” 不能比對 “Bob” 中的 “o”,但是能比對 “food” 中的兩個 o。

{n,} n 是一個非負整數。至少比對 n 次。例如, “o{2,}” 不能比對 “Bob” 中的 “o”,但能比對 “foooood” 中的所有 o。 “o{1,}” 等價于 “o+” 。 “o{0,}” 則等價于 “o*”。

{n,m} m 和 n 均為非負整數,其中 n<=m。最少比對 n 次且最多比對 m 次。例如, “o{1,3}” 将比對 “fooooood” 中的前三個 o 為一組,後三個 o 為一組。 “o{0,1}” 等價于 “o?”。請注意在逗号和兩個數之間不能有空格。

6、字元類

要想查找數字、字母、數字、空白已經很簡單,因為已經有了對應這些字元集合的元字元,但是如果你想比對沒有預定義元字元的字元集合(比如元音字母 a,e,i,o,u ),應該怎麼辦?

很簡單,你隻需要在方括号裡列出它們就行了,像 [aeiou] 就比對任何一個英文元音字母, [.?!] 比對标點符号( . 或 ? 或 !)。

我們也可以輕松地指定一個字元範圍,像 [0-9] 代表的含意與 \d 就是完全一緻的:一位數字;同理 [a-z0-9A-Z_] 也完全等同于 \w (如果隻考慮英文的話)。

下面是一個更複雜的表達式:\(?0\d{2}[) -]?\d{8}。

這個表達式可以比對幾種格式的電話号碼,像 011-22884499 ,或 0845652452 等。我們對它進行一些分析吧:首先是一個轉義字元 (,它能出現 0 次或 1 次 (?),然後是一個 0,後面跟着 2 個數字 (\d{2}),然後是)或-或空格中的一個,它出現 1 次或不出現(?),最後是 8 個數字(\d{8})。

7、反義

有時需要查找不屬于某個能簡單定義的字元類的字元。比如想查找除了數字以外,其它任意字元都行的情況,這時需要用到反義

\w 比對任意不是字母,數字,下劃線,漢字的字元

\s 比對任意不是空白符的字元

\D 比對任意非數字的字元

\B 比對不是單詞開頭或結束的位置

[^x] 比對除了 x 以外的任意字元

[^aeiou] 比對除了 aeiou 這幾個字母以外的任意字元

例子:

\S+ 比對不包含空白符的字元串。 <a[^>]+> 比對用尖括号括起來的以 a 開頭的字元串

8、分組

我們已經提到了怎麼重複單個字元(直接在字元後面加上限定符就行了);但如果想要重複多個字元又該怎麼辦?可以用小括号來指定子表達式(也叫做分組),然後就可以指定這個子表達式的重複次數了,也可以對子表達式進行其它一些操作。

(\d{1,3}\.){3}\d{1,3} 是一個簡單的 IP 位址比對表達式。要了解這個表達式,請按下列順序分析它:\d{1,3} 比對 1 到 3 位的數字,(\d{1,3}\.){3} 比對三位數字加上一個英文句号(這個整體也就是這個分組)重複 3 次,最後再加上一個一到三位的數字(\d{1,3})。

可是也将比對256.300.777.888這種不可能存在的 IP 位址。如果能使用算術比較的話,或許能簡單地解決這個問題,但是正規表達式中并不提供關于數學的任何功能,是以隻能使用冗長的分組,選擇,字元類來描述一個正确的 IP 位址:((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)。

了解這個表達式的關鍵是了解2[0-4]\d|25[0-5]|[01]?\d\d?,這裡就不細說了,大家應該能分析得出來它的意義。

9、貪婪與懶惰

當正規表達式中包含能接受重複的限定符時,通常的行為是比對盡可能多的字元。以這個表達式為例: b.*c ,它将會比對最長的以 b 開始,以 c 結束的字元串。如果用它來搜尋 babac 的話,它會比對整個字元串 babac 。這被稱為貪婪比對。

有時,我們更需要懶惰比對,也就是比對盡可能少的字元。前面給出的限定符都可以被轉化為懶惰比對模式,隻要在它後面加上一個問号 ? 。這樣 .*? 就意味着比對任意數量的重複,但是在能使整個比對成功的前提下使用最少的重複。現在看看懶惰版的例子吧:

a.*?b 比對最短的,以 a 開始,以 b 結束的字元串。如果把它應用于 aabab 的話,它會比對 aab(第一到第三個字元)和 ab( 第四到第五個字元)。

限定符 描述

*? 重複任意次,但盡可能少重複

+? 重複 1 次或更多次,但盡可能少重複

?? 重複 0 次或 1 次,但盡可能少重複

{n,m}? 重複 n 到 m 次,但盡可能少重複

{n,}? 重複 n 次以上,但盡可能少重複

10、處理選項

上面介紹了幾個選項如忽略大小寫,處理多行等,這些選項能用來改變處理正規表達式的方式。下面是 .Net 中常用的正規表達式選項:

IgnoreCase 比對時不區分大小寫。

Multiline 更改 ^ 和 的精确含意是:比對 \n 之前的位置以及字元串結束前的位置.)

Singleline 更改 . 的含義,使它與每一個字元比對(包括換行符 \n)

IgnorePatternWhitespace 更改 . 的含義,使它與每一個字元比對(包括換行符 \n )

ExplicitCapture 僅捕獲已被顯式命名的組。

一個經常被問到的問題是:是不是隻能同時使用多行模式和單行模式中的一種?

答案是:不是。這兩個選項之間沒有任何關系,除了它們的名字比較相似(以至于讓人感到疑惑)以外。

11、提示

正規表達式内容還有很多,筆者這裡隻列舉常用部分,讀者若想進一步學習,可在微軟專業正規表達式學習網站學習:

https://docs.microsoft.com/zh-cn/dotnet/standard/base-types/regular-expressions?redirectedfrom=MSDN

正規表達式文法支援情況如下圖:

一文掌握字元串之正規表達式,值得收藏!

繼續閱讀