正則表達式必知必會
匹配單個字符
匹配純文本
相當于文本查找的功能(CMD + F)。但是一般的正則表達式引擎默認返回第一個匹配結(jié)果。
大小寫匹配
匹配任意字符
使用 . 來匹配任意字符。
譬如:使用 yunis. 來匹配,yunis1,yunis2,yunis3都是符合搜索結(jié)果的。
匹配特殊字符
如果需要匹配特殊字符( . 等)就需要使用元字符( \ 反斜杠)來對他進行轉(zhuǎn)義。
譬如:使用 yunis\. 來匹配, yunis. 是符合匹配結(jié)果的。
匹配一組字符
匹配多個字符串中的任意一個
譬如:使用 [abc]hhhh 進行匹配。ahhhh,bhhhh,chhhh 都是符合條件的搜索結(jié)果。
例如字符集合區(qū)間
譬如:使用[a-c]hhh 進行匹配,ahhhh,bhhhh,chhhh 都是符合條件的搜索結(jié)果。
使用 [0-9]hhh 進行匹配 0hhh,1hhh,2hhh 。。。 9hhh 都是符合條件的搜索結(jié)果。
- A-Z 匹配 A 到 Z 的所有大寫字母。
- a-z 匹配 a 到 z 的所有小寫字母。
- A-F 匹配 A 到 F 的所有大寫字母。
- A-z 匹配從
ASCII字符 A 到ASCII字符 z 的所有字母。 - 同一個字符集合里面可以給出多個字符區(qū)間。譬如
[A-Za-z0-9]可以匹配任何字母和數(shù)字。
取非匹配
可以使用元字符 ^ 來標明你想對一個字符集合取非匹配結(jié)果。
譬如:使用 a[\^0-9] 進行匹配, as,ab,ac 都是符合調(diào)劑的搜索結(jié)果,a0,a1,a2不是符合條件的搜索結(jié)果。
使用元字符
對特殊字符使用轉(zhuǎn)義
如果想匹配元字符需要使用 \ 反斜杠來轉(zhuǎn)義。
譬如:匹配 \ 本身就需要轉(zhuǎn)義,需要使用 \\來匹配 \。
匹配 [ 需要使用 \[ 來進行匹配。
匹配空白字符
-
\f換頁符 -
\n換行符 -
\r回車符 -
\t制表符(Tab) -
\v垂直制表符
匹配數(shù)字
-
\d匹配任意一個數(shù)字字符 等價于[0-9] -
\D匹配任意一個非數(shù)字字符 等價于[^0-9]
匹配字母與數(shù)字
-
\w匹配任意一個字母、數(shù)字或者下劃線字符,等價于[a-zA-z0-9_] -
\W匹配任何一個非字母、數(shù)字或者下劃線字符,等價于[^a-zA-z0-9_]
匹配空白字符
-
\s任意一個空白符 ,等價于[\f\n\r\t\v] -
\S任意一個非空白符 ,等價于[^\f\n\r\t\v]
重復匹配
匹配一個或多個字符
想要匹配同一個字符或者字符集合的多次重復,只需要簡單的給這個字符或字符集合加上一個 + 字符作為后綴就行了。
+ 匹配一個或者多個字符(至少一個,不匹配零個字符的情況)。譬如 a 匹配 a 本身,a+ 將匹配一個或多個連續(xù)出現(xiàn)的 a 。類似的 ,[0-9] 匹配任意一個數(shù)字,[0-9]+ 匹配一個或多個連續(xù)的數(shù)字。
匹配郵箱:[\w.]+@[\w.]\.\w+
一般來說,在字符集合里面的元字符將被解釋為普通字符,不需要被轉(zhuǎn)義,但轉(zhuǎn)義了也沒有壞處。 [\w.] 與 [\w\\.] 是一樣的。
+ 是一個元字符。如果需要匹配 + 本身,需要轉(zhuǎn)義。
匹配零個或者多個字符
+ 匹配一個或者多個字符,* 匹配零個或者多個字符。
* 是元字符,匹配他本身需要轉(zhuǎn)義。
匹配零個或者一個字符
? 元字符 ? 的意思是匹配一個字符一次或者零次。
譬如:https? 進行匹配,http 和 https 都是符合條件的搜索結(jié)果。
匹配重復次數(shù)
-
+和*匹配的字符個數(shù)沒有上限。我們無法為他們將匹配的字符個數(shù)設(shè)定一個最大值。 -
+、*和?至少匹配零個或者一個字符。無法為他們匹配的字符個數(shù)設(shè)定一個最小值。 - 如果只使用
+和*我們無法將他們匹配的字符個數(shù)設(shè)定為一個精確的數(shù)字。
為重復匹配設(shè)定一個精確的值
使用 {6} 表示前一個字符匹配6次。
譬如 y{6} 進行匹配,yyyyyy 是符合條件的匹配結(jié)果。
為重復匹配設(shè)定一個區(qū)間
譬如使用 y{2,4} 進行匹配,yy,yyy,yyyy 都是符合條件的搜索結(jié)果。
為重復匹配設(shè)定一個最小重復數(shù)字
譬如使用 y{2,} 進行匹配,yy,yyy,yyyy,yyyyyyyyyyyy 都是符合條件的搜索結(jié)果。
這個正則的意思是y至少重復2次。
防止過度匹配
下面一段文本 <b>1234</b>qweqweqweqweqweqweqw<b>5678</b>
當我們使用 <b>.*</b> 進行匹配時,我們希望得到的是 <b>1234</b> 和 <b>5678</b> ,但是我們得到的是 <b>1234</b>qweqweqweqweqweqweqw<b>5678</b>。
這是因為什么呢?
因為 * 和 + 都是 貪婪型 的元字符,它們進行匹配時的模式是多多益善而不是適可而止。它們會盡可能的從一段文本的開頭一直匹配到這段文本的結(jié)尾,而不是從文本的開頭匹配到第一個匹配時為止。
當不需要這種貪婪型模式時怎么辦?使用它們的懶惰型版本,懶惰型版本會盡可能少的匹配字符。
| 貪婪型元字符 | 懶惰型元字符 |
|---|---|
* |
*? |
+ |
+? |
{n,} |
{n,}? |
當我們使用 <b>.*?</b> 進行匹配時,就能得到的是 <b>1234</b> 和 <b>5678</b> 的匹配結(jié)果。
位置匹配
單詞邊界
文本 dog hjhjdogkjhkj hhh dogs. 當我們使用 dog 進行匹配時,我們只希望匹配到第一個單詞 dog,但是匹配的結(jié)果是 dog hjhjdogkjhkj hhh dogs.
這個時間就需要使用單詞邊界來限定了,單詞邊界由限定符 \b 表示。
當我們使用 \bdog\b 來匹配時,只會匹配到第一個 dog 單詞。
字符串邊界
-
^匹配一個字符串的開始。 -
$匹配一個字符串的結(jié)尾。 -
(?m)匹配一行的開始。
使用子表達式
子表達式是一個更大的表達式的一部分;把一個表達式劃分為一系列子表達式的目的是為了把那些子表達式當作一個獨立的元素來使用。子表達式必須使用 () 括起來。
譬如:使用
(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}((1\d{1,2})|(\d{1,2})|(2[0-4]\d)|(25[0-5]))
來匹配合法 IP 地址。
這個正則的意思是:
192.168.155.149
- 任何一個1位或者2位數(shù)字
\d{1,2}) - 任何一個以1開頭的3位數(shù)字
(1\d{2}) - 任何一個以2開頭、第2位數(shù)字在0-4之間的3位數(shù)字
(2[0-4]\d)。 - 任何一個以25開頭、第3位數(shù)字在0-5之間的3位數(shù)字
(25[0-5])。 - 然后拼接上
.. - 然后重復3次。
- 最后拼接上一個1到3位數(shù)字,這個數(shù)字是:
- 任何一個1位或者2位數(shù)字
\d{1,2}) - 任何一個以1開頭的3位數(shù)字
(1\d{2}) - 任何一個以2開頭、第2位數(shù)字在0-4之間的3位數(shù)字
(2[0-4]\d)。 - 任何一個以25開頭、第3位數(shù)字在0-5之間的3位數(shù)字
(25[0-5])。
- 任何一個1位或者2位數(shù)字
回溯引用:前后一致匹配
先看一個正則表達式:
[ ]+(\w+)[ ]+\1
這個表達式的意思是首先 [ ]+ 匹配一個或者多個空格,然后 (\w+) 匹配一個或者多個字母或者數(shù)字字符,然后 [ ]+ 匹配一個或者多個空格,最后 \1 的意思是一個回溯引用,它引用的是前面的表達式的第一個表達式。同理 \2 表示引用的是前面第2個表達式,\3 表示引用的是前面第3個表達式.
(Y)(N).+\2 這個表達式的意思是:
首先匹配一個字母Y,然后匹配一個字母N,然后匹配一個或者多個任意字符,最后的 \2 表示引用之前的第2個表達式,這里引用的就是 (N) 表達式。
這種引用是基于位置的引用,如果表達式的位置發(fā)生變化,可能就導致引用失效。
現(xiàn)在一些比較新的正則表達式支持“命名捕獲”,支持位子表達式起一個唯一的名字,可以重復使用。
回溯引用在替換操作中的作用
在替換操作中,回溯引用可以起到很大的作用。
譬如下面一段文本
186-123-425
345-456-132
132-345-234
是以XXX-XXX-XXX 的形式來展示的,如果想改成 (XXX)(XXX)(XXX) 的話,只需要先使用 (\d{3})(-)(\d{3})(-)(\d{3})
把之前的符合替換規(guī)則的文本找出來,然后使用 ($1)($3)($5) 進行替換就行了。
(186)(123)(425)
(345)(456)(132)
(132)(345)(234)
大小寫裝換
| 元字符 | 說明 |
|---|---|
\E |
結(jié)束 \L 或者 \U 裝換 |
\l |
把下一個字符裝換為小寫 |
\L |
把 \L 到 \E 之間的字符全部裝換為小寫 |
\u |
把下一個字符裝換大寫 |
\U |
把 \U 到 \E 之間的字符全部裝換為大寫 |
下面是一段 html 文本:
<html>
<head>
<title>Head First Lounge</title>
</head>
<body>
<h1>Welcome to the New and Improved Head First Lounge</h1>
<p></p>
</body>
</html>
當我們想把 h1標簽中的文本全部改為大寫時,可以這樣操作。
(<h1>)(.*?)(</h1>)
然后
$1\U$2\E$3
前后查找
向前查找
從語法上看,一個向前查找模式其實就是一個以 ?= 開頭的子表達式,需要匹配的文本跟在 = 后面。
文本:
http://www.baidu.com
https://yunissong.github.io/
當使用 .+(?=:) 查找時,: 前面的 http https 會被匹配出來。
向后查找
向后查找的操作符是 ?<=。
文本:
http://www.baidu.com
https://yunissong.github.io/
當使用 (?<=:).+ 查找時, : 后面的 //www.baidu.com //yunissong.github.io/ 會被匹配出來。
前后查找
<h1>welcome to the New and Improved Head First Lounge</h1>
使用 (?<=<h1>)(.*?)(?=</h1>),就能把 h1 標簽之間的內(nèi)容匹配出來。
對前后查找取非
之前看到過對字符集合取非使用的是 ^ 操作符。但是對去前后查找,取非是使用 ! 來替換 = 取非。
| 操作符 | 說明 |
|---|---|
(?=) |
正向前查找 |
(?!) |
負向前查找 |
(?<=) |
正向后查找 |
(?<!) |
負向后查找 |
嵌入條件
(123)456-7890 和 123-456-7890 都是可以接受的北美號碼,而1234567890、(123)-456-7890和(123-456-7890)雖然包含了正確的數(shù)字,但是格式不對,如果我們使用下面的正則來匹配:
文本:
123-456-7890
(123)456-7890
(123)-456-7890
(123-456-7890
1234567890
123 456 7890
正則:
\(?\d{3}\)?-?\d{3}-\d{4}
這段正則的意思:
-
\(?匹配了一個可選左括號; -
\d{3}匹配了3位數(shù)字; -
\)?匹配了一個可選右括號 -
-?匹配了一個可選的連字符; -
\d{3}匹配了3位數(shù)字; -
-匹配了一個連字符; -
\d{4}匹配了4位數(shù)字。
符合匹配的結(jié)果有:
123-456-7890
(123)456-7890
(123)-456-7890
(123-456-7890
并不能排除不正確格式的電話號碼。
正則表達式里的條件
正則表達式里的條件要用 ? 來定義。
之前我們已經(jīng)見過幾種特定的條件了。
-
?匹配前一個字符或者表達式,如果它存在的話; -
?=和?<=匹配前面或者后面的文本,如果它存在的話; - 嵌入語法也使用了
?- 根據(jù)一個回溯引用來進行條件處理。
- 根據(jù)一個前后查找來進行條件處理。
回溯引用條件
回溯引用條件只有在前面一個表達式搜索取得成功的情況下才允許使用的一個表達式。
需要把文本里面 <IMG> 標簽全都找去來,不僅如此,如果某個 <IMG> 標簽是一個鏈接 (<A> 標簽包裹),還需要把鏈接匹配出來。
文本:
<!-- Nav bar -->
<TD>
<A href="/home"></A>

<A href="/home"></A>

<A href="/home"></A>

</TD>
正則:
(<[Aa]\s+[^>]+>\s*)?<[Ii][Mm][Gg]\s+[^>]+>(?(1)\s*</[Aa]>)
解析:
-
(<[Aa]\s+[^>]+>\s*)?將匹配一個<A>或<a>標簽,以及標簽內(nèi)部的任何屬性,這個標簽可有可無,因為后面跟了一個?; -
<[Ii][Mm][Gg]\s+[^>]+>匹配一個 <IMG> (大小寫均可) 標簽以及任意屬性。 -
(?(1)\s*</[Aa]>)是一個回溯引用條件,?(1)的意思是:如果第一個回溯引用存在這個例子中就是前面的<A>標簽),則使用\s*</[Aa]>繼續(xù)匹配。只有前面的<A>標簽匹配成功,才繼續(xù)進行后面的匹配。如果(1) 存在,\s*</[Aa]>將匹配 結(jié)束標簽 </A> 之前的任意空白字符。
回溯引用條件否表達式
形式:
(?(backreference)true-regex|false-regex)
如果 backreference 成立 執(zhí)行 true-regex ,不成了,執(zhí)行 false-regex。
!!!! 本來想用這個查找下代碼里面有多少是換行后的
{多少是空格后的{,Xcode竟然不支持條件查找!!!.
再來看之前那個電話號碼的例子,正則改為:
(\()?\d{3}(?(1)\)|-)\d{3}-\d{4}
解析:
-
(\()?匹配了一個可選左括號; -
\d{3}匹配了3位數(shù)字; -
(?(1)\)|-)-
?(1)判斷前面的條件是否成立,(是否存在。 - 存在 執(zhí)行
\); - 不存在,執(zhí)行
-。
-
-
\d{3}匹配了3位數(shù)字 -
-匹配了一個連字符; -
\d{4}匹配了4位數(shù)字。
前后查找條件
其實就是之前的回溯引用差不多,不同的就是,這里的回溯條件使用了自定義的子正則表達式。
d{5}(?(?=-)-\d{4})
(?(?=-)-\d{4}) 的意思是如果 (?=-) 成立,則進行后面 -\d{4} 的匹配。