對於正則,許多程式設計師都覺得它很繁瑣,找不到頭緒。但其實只要明白了基礎語法,正則其實是非常簡單的。學習正規表示式一定要躬行實踐,自己動手來測試的自己表示式,這將大大有益對於正規表示式的掌握。在正文開始前,先給大家推薦一個好用的正則線上測試工具,本文後面將會使用它來對我們編寫的正規表示式做測試:https://c.runoob.com/front-end/854/
正規表示式的基本形式:/pattern/flags
。其中 pattern 是匹配規則,flage 被稱為修飾符。我們先來看一個簡單的範例:
正則:/you/g
文字:If you shed tears when you miss the sun, you also miss the stars
匹配結果:
you
you
you
提示:在每一個範例下面,可以通過上面提供的線上測試工具來自己測試一下,加深理解。線上測試工具的結果:
在上面的範例中,/g
被稱為全域性匹配,是一個常用修飾符,表示匹配字串中的所有元素,即匹配了3個 you
。
修飾符用於標記正規表示式的額外策略,下面四個是常用的修飾符:
字元 | 描述 |
g | 全域性匹配 |
i | 忽略大小寫 |
m | 多行模式 |
s | 該模式下,.會匹配換行符\n |
g 在所有的表示式中基本都需要攜帶,i 望文知義。 m 和 s 我們會在後文中逐漸的認識到,現在不必糾結他們。
元字元這個概念比較難以被理解,通常會直接勸退一批想學正規表示式的人。其實元字元說白了,就是規定一個普通字元具備特殊含義,用來匹配符合這個特殊含義的字元。我們先列舉出常用的一些元字元,逐項來看他們的所具備的特殊含義。
字元 | 描述 |
sea|sky | 匹配 sea 或者 sky,可以匹配若干個,如 sea|sky|stars |
[abc] | 匹配 a 和 b 和 c |
[^abc] | 匹配除了 a 和 b 和 c 之外的字元 |
[a-z] | 匹配 a 到 c 之間的所有小寫字元,也可以使用[0-9]匹配數位範圍 |
[a-z] | 匹配除了 a 到 c 之間的所有小寫字元,同理,也可以匹配數位範圍 |
以上五個選擇分支的匹配規則是很常用的匹配規則。通常,sea|sky
這種匹配方式會被 ()
括起來:
()
也是一種元字元,通常用來把一組匹配規則括起來,表示一個分組。
字元 | 描述 |
. | 匹配任意字元,不包括換行符\n和\r,如果需要匹配 \n和\r ,可以使用修飾符:s |
\d | 匹配一個數位,相當於 [0-9] |
\D | 匹配非數位,相當於 [^0-9] |
\s | 匹配任意空白字元,相當於 [\t\n\r\f\v] |
\S | 匹配非空白字元,相當於 [^\t\n\r\f\v] |
\w | 匹配數位、字母、下劃線中任意一個字元,相當於 [a-zA-Z0-9_] |
\W | 匹配非數位、字母、下劃線中的任意字元,相當於 [^a-zA-Z0-9_] |
\
也是一個基礎元字元,用來將元字元轉換為普通字元,類似於程式語言中的跳脫。如真的要匹配 .
這個字元,應該使用 \.
,否則 .
將會被識別為匹配任意字元。正規表示式中需要的跳脫字元:* . ? + $ ^ [ ] ( ) { } | \
還有一些基礎元字元:邊界元字元。其不佔用字元位置,只是表達一個邊界:
字元 | 描述 |
\b | 匹配位於每個單詞的開始或結束位置 |
\B | 匹配不是單詞開頭和結束的位置,即每個單詞的中間位置 |
^ | 匹配開始位置,多行模式下匹配每一行的開始 |
$ | 匹配結束位置,多行模式下匹配每一行的結束 |
\b
和 \B
比較容易理解,\b
可以粗略的理解就是匹配單詞之間的空格,但是匹配結果不會攜帶這個空格。\B
則是和 \b
相反:
^
和 &
匹配有一個很重要的概念:行。他們表示了一行的開始和結束位置。我們來看一個範例:
可以看到,加入了 ^
後,an
的匹配只會從行首開始匹配,我們這裡加入 i
修飾符,表示不區分大小寫的匹配。&
同理:
為什麼說行是 ^
和 &
的重要概念呢,我們來看一組範例。ok&
可以匹配以 ok
為結束的字元:
這顯然符合我們的預期,但是當我們在加入一行文字,匹配結果就會出現意外:
按照道理說,應該能匹配到兩個 ok
字元,但是這裡只匹配到了最後一行,如果需要匹配多行資料,則需要加入一個修飾符:m (多行模式)。讓我們看一下加入多行模式後的匹配結果:
這樣就符合了我們的預期情況。
字元 | 描述 |
* | 匹配前面的子表示式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等價於{0,}。 |
+ | 匹配前面的子表示式一次或多次。例如,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{0,1} 等價於 'o?'。請注意在逗號和兩個數之間不能有空格。 |
在該匹配的模式下,還有一個特殊的元字元:?
。被稱為非貪婪模式。注意,該 ?
和上文的匹配零次或一次完全不同。非貪婪模式的元字元 ? 只能跟在上述六個的重複匹配後面。例如,我們想要匹配出一段文字中的以 http 或者 https 開頭的圖片地址:
範例文字中存在兩個 .png
的圖片,我們使用 (http:https)
來匹配 http 或者 https 開頭,然後使用 .
來匹配所有字元,再加上 +
,表示一次或多次匹配,最後使用 (.png)
表示匹配 .png 結束。但這樣的結果就是當我們遇到第一個 .png 時,.+
也會匹配到 .png,所以就導致了獲取了錯誤的匹配結果。遇到這種情況,我們就要使用非貪婪模式,在 +
後面加上 ?
:
可以看到,此時獲取了我們想要的結果。非貪婪模式下,會盡可能少的匹配字串。
到此,我們已經掌握了正規表示式的基礎,可以找一些實際的業務需求來加深理解。比如說檢測郵箱地址是否合法:
\w
會匹配所有數位、字母下劃線, +
表示至少需要匹配到一個字元,我們要求它需要到 @ 時停下,所以加上 ? 非貪婪模式。這樣就可以成功匹配到 tyyn1022@ 。繼續檢測後面的是否符合要求,同樣使用 \w+?
匹配域名名稱,\.
表示匹配文字 . ,最後加上 (com|net|top|org),即要求頂級域名必須是 com、net、top、org。這樣就匹配到了剩餘的部分:163.com
零寬斷言是正規表示式的高階用法,它一種特殊的元字元。實際作用就如同它的名字:零寬與斷言。簡而言之,就是匹配某個字元的前後,卻又不想匹配到這個字元本身(零寬的意思)。零寬斷言分為四種:
字元 | 描述 |
(?=pattern) | 正向先行斷言。例如,foo(?=bar) 會匹配 foobar 中的 foo。 |
(?!pattern) | 反向先行斷言。例如,foo(?!bar) 會匹配 foobaz 中的 foo,但不會匹配 foobar 中的 foo。 |
(?<=pattern) | 正向後行斷言。例如,(?<=foo)bar 會匹配 foobar 中的 bar,但不會匹配 bazbar 中的 bar,因為它前面不是 foo。 |
(?<!pattern) | 反向後行斷言。例如,(?<!foo)bar 會匹配 bazbar 中的 bar,但不會匹配 foobar 中的 bar。 |
我們來舉一個實際的例子,我們需要從一段富文字文字中匹配所有以 http:// 或者 https:// 開頭,以 .png 或者 .jpg 結尾的圖片地址,但是要求不能把 http:// 和 https:// 匹配進去
/((?<=http://)|(?<=https://)).+?(.png|.jpg)/ig
該正規表示式即可達到我們想要的效果:
在該表示式中分為三段:
((?<=http://)|(?<=https://))
,正向後行斷言,表示匹配以 http:// 或者 https:// 開頭的字元.+?
,匹配除了換行符 \n 和 \r 之外的所有字元至少一次,且是非貪婪模式,為什麼這裡使用貪婪模式,可以自己動手試一下(.png|.jpg)
表示以 .png 或者 .jpg 結尾