正則表達式

在編寫處理字符串的程序或網頁時,經常會有查找符合某些複雜規則的字符串的需要。正則表達式就是用於描述這些規則的工具。換句話說,正則表達式就是記錄文本規則的代碼,最多的用途就是匹配帶有一定規律的字符串

常用元字符(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 | 匹配字符串 | 4432 | 和 | 4432出 | -> 前者成功,後者失敗,前者是因爲'出''4'並不是同種類型的字符,所以這裏有三個單詞"44""出""32";而後者"4432"是一個單詞,並不滿足"44"之後是單詞結尾
^44 | 匹配字符串 | 4432 | 和 | 出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)1234567802012345678

分枝條件

不難看出之前那個表達式\(?0\d{2}[) -]?\d{8}同樣可以匹配例如(020-12345678010)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 對象

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章