在編寫處理字符串的程序或網頁時,經常會有查找符合某些複雜規則的字符串的需要。正則表達式就是用於描述這些規則的工具。換句話說,正則表達式就是記錄文本規則的代碼,最多的用途就是匹配帶有一定規律的字符串
常用元字符(metachracter)
.
- 匹配除換行(\r\n
)以外的任意字符\w
- 匹配包括下劃線的任何單詞字符(unicode),類似但不等價於[A-Za-z0-9_]
\s
- 匹配任意的不可見字符,等價於[\f\n\r\t\v]
\d
- 匹配數字\b
- 匹配單詞的開始或結束^
- 匹配字符串的開始$
- 匹配字符串的結束
\b
與^
和$
看起來\b
與^
和$
相同,但是是有差異的
\b
是表示一個單詞的邊界,一個單詞例如一連串的英文和數字、或者一句中文^
和$
是表示一個字符串的開始和結尾,分割字符串的僅是那些不可見字符
/b44/b | 匹配字符串 | !@#$%44&*() | -> 匹配成功,因爲"44"這個字符串的左右都不是英文數字,所以這裏"44"就是一個單詞
^44$ | 匹配字符串 | !@#$%44&*() | -> 匹配失敗,因爲這個字符串的開頭不是'4'而是'!',結尾不是'4'而是')'
/b44/b | 匹配字符串 | 44出32 | 和 | 4432出 | -> 前者成功,後者失敗,前者是因爲'出'和'4'並不是同種類型的字符,所以這裏有三個單詞"44"和"出"和"32";而後者"4432"是一個單詞,並不滿足"44"之後是單詞結尾
^44 | 匹配字符串 | 44出32 | 和 | 出4432 | -> 前者成功,後者失敗,這裏只用考慮整個字符串是不是以"44"開頭的
另外有些正則表達式處理提供 多行(Multiline) 選項,使得
^
和$
也匹配\n
和\r
的前後位置
JavaScript RegExp \b 元字符
正則匹配中 ^ $ 和 \b 的區別
JavaScript multiline 屬性
字符轉義
和很多語言一樣,也是用\
符號來轉義,因此在JavaScript當中有時候可能要兩次轉義
重複限定符
元字符只是匹配一個位置或者字符,當我們想匹配多個或者不確定個數元字符時,就要搭配上下面這些限定符使用了
*
- 重複次數 >= 0+
- 重複次數 >= 1?
- 重複次數 == 0 或 重複次數 == 1{n}
- 重複次數 == n{n,}
- 重複次數 >= n{n,m}
- m >= 重複次數 >= n
0\d{2}-\d{8} - 匹配上010-12345678
\ba\w*\b - 匹配一個單詞以'a'開頭,之後任意個數字字母,直到單詞的結尾
Windows\d+ - 匹配'Windows'後面跟上不少於一個數字
字符類
如果沒有特定的元字符,比如只匹配’c’、’m’、’d’,此時用[]
框住,就像一個集合一樣
[cmd] - 只匹配'c'或'm'或'd'這三個字符
[.!?] - 只匹配'.'或'!'或'?'這三個標點符號(這裏的'.'必然不是元字符)
也可以在框中指定一個範圍
[0-9] - 匹配數字
\(?0\d{2}[) -]?\d{8}
首先"\(?"說明一開始可以出現0次或1次'('
然後是必須要出現一個'0'
接着是兩個數字
之後是出現0次或1次'('或' '或'-'
最後是8個數字
可以匹配:020-12345678、(020)12345678、02012345678
分枝條件
不難看出之前那個表達式\(?0\d{2}[) -]?\d{8}
同樣可以匹配例如(020-12345678
或010)12345678
這種不正確的格式,正則表達式中用|
來表示分枝,只要滿足一個分支都算匹配成功,且當從左至右第一個分支匹配成功後,後面的分支都不會參與測試
\(0\d{2,3}\)[- ]?\d{8}|0\d{2,3}[- ]?\d{8} - 解決上面錯誤格式問題,以及將區號擴展爲3或4位
分組
使用()
來指定子表達式的方式叫做分組,這樣我們就可以加上限定符重複多個字符了
(\d{1,3}\.){3}\d{1,3} - 一個用來簡單匹配IP地址的表達式,這裏重複\d{1,3}\.這個組3次,最後加上\d{1,3},但是我們知道IP地址每位最大是255,因此需要分枝
((2[0-4]\d|25[0-5]|[01]?\d?\d)\.){3}(2[0-4]\d|25[0-5]|[01]?\d?\d) - 這樣就可以解決最大數字問題了
反義
查找不屬於某個元字符(或者字符類、分組)的字符,有以下反義
\W
- 匹配不包括下劃線的任何非單詞字符(unicode)\S
- 匹配任意不是空白字符的字符\D
- 匹配任意不是數字的字符\B
- 匹配不是單詞開頭或結束的位置[^xyz]
- 匹配除’x’或’y’或’z’以外的任意字符
\S+ - 匹配不包含空白字符的字符串
<a[^>]+> - 匹配用'<>'框住且以'a'開頭的字符串
後向引用
分組後,從左至右以(
作爲分組標誌,默認從1開始作爲組號遞增,後向引用用於重複搜索前面某個分組匹配的文本
\b(\w+)\b\s+\1\b - 可以匹配類似"go go"(其中"\1"爲"go")、"cmd cmd"(其中"\1"爲"cmd"),但不能匹配"cmd cmdd"、"123 234"
當然可以自己指定子表達式(某個分組)的組名,使用(?<Word>\w+)
或(?'Word'\w+)
,這樣相當於將子表達式\w+
命名爲Word
了,反向引用語法爲\k<word>
,這樣上面那個例子可以寫爲
\b(?<cmd>\w+)\b\s+\k<cmd>\b
其實組號分配並沒那麼簡單,組號0代表整個正則表達式,組號分配時會掃描表達式兩遍,第一遍只給未命名組分配,第二遍只給命名組分配
零寬斷言
類似於\b
、^
、$
用於匹配一個位置,並且應該滿足一定條件(斷言)
零寬度正預測先行斷言
(?=exp)
- 匹配滿足表達式之前的位置
\b\w+(?=ing\b) - 匹配一個以"ing"結尾的單詞的前面部分
零寬度正回顧後發斷言
(?<=exp)
- 匹配滿足表達式之後的位置
(?<=\bre)\w+\b - 匹配一個以"re"開頭的單詞的後面部分
(?<=\s)\d+(?=\s) - 匹配兩端都是空格字符的一串數字
零寬度負預測先行斷言
表達式\b\w*q[^u]\w*\b
本來只是想確定這個單詞中含有’q’且後面不是’u’,但是[^u]
必須匹配上一個字符,因此這一串不能匹配上”aiq”這種以’q’結尾的單詞,卻能匹配上”aiq be”這種兩個單詞以空格分開且前一個單詞結尾是’q’的字符串,如果只用之前所說的方法,又要寫上一長串的分枝了
(?!exp)
- 匹配不滿足表達式之前的位置
\d{3}(?!\d) - 匹配一串數字的最後三個數字
\b((?!abc)\w)+\b - 匹配一個不包含"abc"子字串的單詞
\b([^q\s]*q(?!u)[^q\s]*)+\b - 就解決上面那個問題了
零寬度負回顧後發斷言
(?<!exp)
- 匹配不滿足表達之後的位置
(?<![a-z])\d{7} - 匹配7個連續的數字,且第一個數字前不是小寫字母
(?<=<(\w+)>).*(?=<\/\1>) - 匹配不包含屬性的簡單HTML標籤內裏的內容(例如<b>123</b>中的123)
註釋
(?#comment) - 最好啓用忽略模式裏的空白符,這樣表達式中可以添加任意的空格、換行字符,且#
後面直到換行之前的都將被當作註釋,例如之前(?<=<(\w+)>).*(?=<\/\1>)
可以寫爲
(?<= # 斷言要匹配的文本的前綴
<(\w+)> # 查找尖括號括起來的字母或數字(即HTML/XML標籤)
) # 前綴結束
.* # 匹配任意文本
(?= # 斷言要匹配的文本的後綴
<\/\1> # 查找尖括號括起來的內容:前面是一個"/",後面是先前捕獲的標籤
) # 後綴結束
分組語法整理
下面整理以上常用的分組語法(其中exp
代表表達式)
(exp)
- 匹配exp,並捕獲匹配得到的文本到自動命名的組內(?<name>exp)
- 匹配exp,並捕獲匹配得到的文本到自主命名爲name
的組內(?:exp)
- 匹配exp,但不捕獲文本,也不分配組號(?=exp)
- 匹配滿足表達式之前的位置(?<=exp)
- 匹配滿足表達式之後的位置(?!exp)
- 匹配不滿足表達式之前的位置(?<!exp)
- 匹配不滿足表達式之後的位置
限定符的貪婪與懶惰
- 貪婪 - (默認)匹配儘可能多的字符
- 懶惰 - 匹配儘可能少的字符,在限定符後添加
?
(*?
、+?
、??
、{n,m}?
、{n,}?
)
現有字符串aabab
a.*b - 匹配得到aabab
a.*?b - 匹配得到aab和ab
處理選項
IgnoreCase
- 忽略大小寫Multiline
- 多行模式,使得^
和$
也匹配\n
和\r
的前後位置Singleline
- 單行模式,使得.
與所有字符匹配,包括\n
IgnorePatternWhitespace
- 忽略非轉義空白,並啓用#
註釋ExplicitCapture
- 顯式捕獲,僅捕獲已被顯式命名的分組
平衡組/遞歸匹配
考慮到有嵌套關係的層次結構,例如xx <aa <bbb> <bbb> aa> yy
這個字符串怎樣匹配到最外層的<>
所含內容
以下是.Net Framwork支持的語法結構
(?'group')
- 把捕獲的內容命名爲group並壓入堆棧(Stack)(?-group)
- 從堆棧頂彈出名爲group的捕獲內容,若堆棧爲空,則此次分組匹配失敗(?(group)yes|no)
- 如果堆棧上存在以group爲名的捕獲內容,則匹配yes部分的表達式,否則匹配no部分的表達式(?!)
- 由於沒有後綴表達式,試圖匹配總是失敗
<[^<>]*(((?'Open'<)[^<>]*)+((?'-Open'>)[^<>]*)+)*(?(Open)(?!))>
< #最外層的左括號
[^<>]* #最外層的左括號後面的不是括號的內容
(
(
(?'Open'<) #碰到了左括號,在黑板上寫一個"Open"
[^<>]* #匹配左括號後面的不是括號的內容
)+
(
(?'-Open'>) #碰到了右括號,擦掉一個"Open"
[^<>]* #匹配右括號後面不是括號的內容
)+
)*
(?(Open)(?!)) #在遇到最外層的右括號前面,判斷黑板上還有沒有沒擦掉的"Open";如果還有,則匹配失敗
> #最外層的右括號
js中運用正則表達式
RegExp
對象
js中正則表達式對象,通過下面兩種方式可以創建
var re = new RexExp(str, attr) // str是正則表達式字符串,attr是可選參數
var re = /str/attr // str是正則表達式,用兩個/包圍,後面attr是可選參數
可選參數(修飾符)
i
- 忽略大小寫敏感g
- 執行全局匹配m
- 執行多行匹配
常用的是
g
,比如用/\d+/
和/\d+/g
去匹配字符串1a2b3d4c
,前者只能匹配到一個結果['1']
,後者可以匹配到四個結果['1', '2', '3', '4']
可以使用正則的函數
以下樣例中用 re
表示正則表達式,用s
表示字符串
s.match(re)
匹配字符串,返回匹配結果
s.search(re)
返回匹配上的第一個字串的偏移量
s.replace(re, text)
將匹配結果替換爲其他文本
s.split(re)
將匹配的子串作爲分隔符,返回分割後的數組(此函數無視g
修飾符,默認全文替換)
re.exec(s)
對字符串查找,返回找到的子字符串和偏移量(此函數無視g
修飾符,只返回第一個結果)
re.test(s)
檢測字符串,僅當完全匹配上時返回true
資料整理
正則表達式30分鐘入門教程
在線正則表達式測試
正則表達式_百度百科
js正則函數match、exec、test、search、replace、split使用介紹集合
JavaScript RegExp 對象