開始真正瞭解RE是從這篇開始的。RE實在是博大精深,且把之前學到的東西再整理回憶下了。
Regex就是記錄文本規則的代碼。
前面那句話說的好,RE是描述的一種字元排列的規則,這就有兩個要素:
1、表達形式:
表達形式包含了主體和組合方式
a) 主體:各種字元,不管一個Regex的字元代表了一個還是一類字元,總之,這些主體是可以看到的
b) 組合方式:也就是位置,同樣的字元,位置不同,排列不同,表示的字串也不同
所以說,表達形式這塊的內容,可以直接地從RE匹配的結果中看出來。
. |
匹配除分行符號"\n"之外的任一字元 |
\b |
代表單詞的開頭或結尾,即單詞的分界處[ \b匹配這樣的位置:它的前一個字元和後一個字元不全是\w ] RE中的單詞:不少於一個的連續的\w |
\d |
一位元字(0,1,2……9) |
\s |
任意空白符:空格,定位字元,分行符號,中文全形空格 |
\w |
字母或數字或底線或漢字等 |
^ |
字串的開始 |
$ |
字串的結束 |
+ |
前面內容匹配1次或多次 |
* |
表示數量:前邊的內容可以連續重複使用任意次是整個運算式匹配,0次或多次 |
? |
重複0次或1次 |
{n} |
重複n次 |
{n,} |
重複n次或多次 |
{n,m} |
重複n次到m次 |
\ |
匹配某些特殊符號時,取消符號的特殊含義: \--\\,*--\*,.--\.,(--\(,)--\) |
[aeiou] |
匹配一個字元,候選值為aeiou |
[0-9] |
匹配一個數字,候選值為0,1,2,3,4,5,6,7,8,9 |
反義:大寫表示與小寫相反的含義
\W |
任意不是字母、數字、底線、漢字的字元 |
\S |
任意非Null 字元 |
\D |
任意非數字字元 |
\B |
不是單詞開頭或結束的位置 |
[^x] |
除x意外的任一字元 |
[^aeiou] |
除aeiou以外的任一字元 |
|
2、表達方式:
在表達形式之上,如何合理、精確、簡短地描述規則,就是表達方式的範疇,比如:分支、分組、後向引用、零寬斷言、貪心與懶惰、遞迴匹配等。
可能有人會說,上一行提到的這些東西,也有是直接反映在結果中的。我相說的是,表達方式各種方法的使用,使表達形式更簡潔、精確,其實有些沒有也行,比如:後向引用,很顯然的一個例子。
分枝條件 |
用“|”將不同的規則分隔開 eg. > 國內固定電話:0\d{2}-\d{8} | 0\d{3}-\d{7} 以“-”分割,3位區號和四位區號 > 國內固定電話,3位或4位區號,區號可以用“-”與號碼隔開,也可以用()隔開,也可以用空格,也可以什麼都沒有: \(?0\d{2}\)?[- ]?\d{8} | \(?0\d{3}\)?[- ]?\d{7} bug: 對01234567890沒法分出3位還是4位區號;對(012-34567890無匹配成功 ((\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$)
支援手機號碼,3-4位區號,7-8位直播號碼,1-4位分機號 > 美國郵遞區號:\d{5}-\d{4} | \d{5} 9位,前5位後有“-”分割,或是只有5位 (注意各個條件的順序 如果是\d{5} | \d{5}-\d{4},則值匹配5位的郵編或位的前5位,在有分枝時,只要滿足一個就算匹配完成,後面會提到的Lazy原則) |
分組 |
重複一組字元,用()來指定子運算式,即為分組 eg. > IP地址: ((2[0-4]\d | 25[0-5] | [01]?\d\d?)\.){3}(2[0-4]\d | 25[0-5] | [01]?\d\d?) |
後向引用 |
指定子運算式(分組)後,用編號來引用前面的分組,自添加的分組編號預設從1開始,\1 表示分組1匹配的文本。分組0匹配整個Regex。 eg. 重複的單詞,如"go go" (\b(\w+)\b)\s+\1\b 可以自己給分組指定組名:(?<GroupName>expr)或(?'GroupName'expr) [ 組號匹配過程要掃描兩邊:1、掃描未命名的分組;2、掃描命名分組 ] eg. > IP地址: ((?<IP>2[0-4]\d | 25[0-5] | [01]?\d\d?)\.){3}\k<IP> |
零寬斷言 |
“零寬”表示這個文法不在匹配串中佔用任何字元 “斷言”表示條件不滿足即可退出 確保匹配串附近出現了某些字元 (?=exp) 零寬度正預測先行斷言:斷言自身出現的位置的後面能匹配運算式exp 匹配串後面是exp eg. > 一篇文章中結尾為ing的詞中除ing之外的部分: \b\w+(?=ing\b?) Rolling in the deep 它匹配 Roll (?<=exp) 零寬度正回顧後發斷言:斷言自身出現的位置的前面能匹配運算式exp 匹配串前面是exp eg. > 匹配以re開頭的詞中除re之外的部分: (?<=\bre)\w+\b reading a book 它匹配ading > 以空白符間隔的數字(不包含這些空白符) (?<=\s)\d+(?=\s) |
負向零寬斷言 |
確保匹配串附近沒有出現某些字元 (?!exp) 零寬度負預測先行斷言:斷言此位置的後面不能匹配運算式exp 匹配串後面不能是exp eg. > 後面不是字母u的q的單詞: \b\w*q(?!u)\w*\b (?<!exp) 零寬度負回顧後發斷言:斷言此位置後前面不能匹配運算式exp 匹配串前面不能是exp eg. 前面不是小寫字母的七位元字: (?!<[a-z])\d{7} 不含屬性的簡單HTML標籤裡的內容: (?<=<\w+>).*(?=<\/\1>) |
注釋 |
(?#comment) eg. IP地址: ((?<IP>2[0-4]\d(?#200-249) | 25[0-5](?#250-255) | [01]?\d\d?(?#0-199))\.){3}\k<IP> |
貪心 |
當RE中包含能接受重複的限定符是,通常是在能匹配的前提下,匹配儘可能多的字元。 eg. a.*b 匹配最長的以a開始,以b結束的字串 對aabab,會匹配整個字串 |
懶惰 |
匹配儘可能少的字元 在前面的限定符後面加?即可轉化為懶惰模式 eg. a.*?b 對aabab得到的匹配是aab和ab兩個
| *? |
重複0次或多次,但儘可能少重複 |
| +? |
重複1次或多次,但儘可能少重複 |
| ?? |
重複0次或1次,但儘可能少重複 |
| {n,m}? |
重複n次到m次,但儘可能少重複 |
| {n,}? |
重複n次以上,但儘可能少重複 |
|
最先匹配 |
最先開始的匹配具有最高的優先順序 優先順序高於貪心或懶惰 |
|
上面的思路或許不那麼嚴謹,只是講講我自己按照怎麼樣的邏輯去理解這套東西的。