正則學習筆記正則表達式
元字符學習
元字符能夠先大概的分爲兩類, 一類用來匹配字符或者位置, 一類用來匹配重複次數.測試
字符或位置spa
字符ip
代碼ci |
說明字符串 |
示例it |
\bio |
表明單詞的開頭或者結尾,也就是單詞的分界處. 一般用來精確匹配單詞的時候會用到.table |
如咱們但願匹配'hi'這個單詞, 那麼能夠用 \bhi\b來匹配.這樣只會匹配一個單獨的hi單詞,其它的history/him/fadhifa等都不會匹配. 相似的, 可使用\bhi 和 hi\b 來分別匹配以hi開頭或者以hi結尾的字符 |
|
|
|
\d |
匹配一個數字(0-9). |
好比0\d\d-\d\d\d\d\d\d\d\d 用這個表示0開頭,而後是任意兩位數字, 而後是中劃線-, 而後是8位數字. |
. |
表明除了換行符之外的任意字符 |
\bhi.oo\b 匹配以hi開頭中間有一個任意字符,同時以oo結尾 |
\w |
匹配正常字符, 即 字母/數字/下劃線/漢字 注意此處w爲小寫 不包含特殊字符,好比#$&*?>)之類 |
\whi 匹配hi前面任意字符 |
\s |
匹配任意空白字符 |
\shi\b 匹配任意以hi結尾,而且hi前面有空格的字符 |
^ |
匹配字符串開始 |
以前的都是字符/單詞, 這個用來匹配字符串 |
$ |
匹配字符結尾 |
和^共同組成字符串的開頭結尾|^hi\w$ 匹配以hi開頭,而後是一位任意字符結尾的字符串 |
位置(限定符)
代碼 |
說明 |
示例 |
* |
重複零次或者任意次數 |
此重複包含零次, 好比能夠用 a\w*來匹配a開頭的任意爲字符,包括字符a |
+ |
重複一次或者更屢次,是*的子集,即不包括零次 |
好比能夠用 a\w+來匹配a開頭的任意爲字符,可是不包括字符a |
? |
重複零次或者一次 |
?和+ 一塊兒共同組成* |
{n} |
重複n次數,必須是很精確的n次 |
hi{3} 匹配hiii這樣的字符而不是 hihihi |
{n,} |
重複n次或者跟屢次, 就是時候至少n次 |
fa{2} 會匹配faa 而不會匹配faaa faaaaa, 可是fa{n,} 會匹配這些 |
{n,m} |
重複n-m次之間的次數,包括n和m |
fa{2,3} 會匹配faa faaa 但不會匹配fa faaaa |
字符類
上面咱們已經有了一些對應字符集合的元字符來表明數字字母或者數字, 可是若是咱們但願匹配那些沒有預約義元字符的字符集合的時候應該字母辦呢?
一個簡單的方法就是把須要匹配的字符在方括號裏列出來, 好比[?><]能夠匹配 ?>< 在方括號中的都會陪匹配到.
好比說[0-9]表明的含義其實和 \d 是同樣的, 都是一位數字;[a-z0-9A-Z_]也徹底等同於\w, 不考慮漢字的狀況,由於它包括大小寫字母/數字和下劃線.
(?0\d{2}[) -]?\d{8} 這個複雜的正則的解析以下: (? 表示零個或者一個(,而後是一個0, 而後是兩個數字\d{2}. [) -]? 表示零個或者一個 )或者-或者空格 , 而後是八位數字. 那麼如下都會匹配到 (012)12345678/(01212345678/(012-12345678/012)12345678/01212345678/012-12345678
分支條件
仍是上面這個 (?0\d{2}[) -]?\d{8} 其實咱們是想匹配三位區號+八位電話號碼, 可是不但願出現012)12345678或者(012-12345678 這樣的格式. 那怎麼才能把這樣的排除呢?
這裏咱們須要引入分支條件的概念,指的是一個正則有幾種規則, 只要知足任意一個均可以完成匹配,具體方式就是把多個規則用|分隔開來.
0\d{2}[- ]\d{8}|[(]0\d{2}[)]-\d{8} 這樣能夠分拆成兩部分, 一部分0\d{2}[- ]\d{8} 以0開頭的三位數字, 中間是-或者空格, 而後是八位數字; 另外一部分[(]0\d{2}[)[- ]\d{8} 圓括號包裹的三位數字, 中間是-或者空格, 而後是八位數字. 只要符合這樣標準的都會被上面的正則匹配到
分組
咱們以前已經知道怎麼重複單個字符, 直接在字符或者元字符後面叫上* ? + 等限定符就行了, 那麼若是咱們但願重複多個字符怎麼辦?
能夠用小括號(圓括號)來指定子表達式,也就是分組,而後就能夠指定重複次數了. 好比 hi{3} 匹配hiii (hi){3} 匹配hihihi.
(\d{1,3}.){3}\d{1,3} 是一個初始的ip地址匹配. 首先是\d{1,3} 表示1-3位數字, 而後是 \d{1,3}. 一到三位數字叫一個點,好比256. 365. 256. 等. (\d{1,3}.){3} 表示重複三次 ,也就是 236.236.258.這樣的字符, 而後是\d{1,3} 最後再跟上三位數字. 這樣的正則也會匹配000.203.014.0 這樣的地址
((2[0-4]\d|25[0-5]|[01]?\d\d?).){3}(2[0-4]\d|25[0-5]|[01]?\d\d?) 這是一個比較複雜的正則,咱們須要一步步的來解析. 先將上面的長正則拆分紅 ((2[0-4]\d|25[0-5]|[01]?\d\d?).){3} 和(2[0-4]\d|25[0-5]|[01]?\d\d?) 第一個又能夠拆分爲 (2[0-4]\d|25[0-5]|[01]?\d\d?)和. 那麼核心已經很明顯了, 就是 (2[0-4]\d|25[0-5]|[01]?\d\d?) 又能夠分爲三個分支條件 2[0-4]\d 和25[0-5] 和[01]?\d\d? 第一個表示從200-249的數字, 第二個是250-255的數字, 第三個是從000-199的數字, 結合起來就是0-255的數字,也就是ip地址的範圍. 而後前面三個加點. 最後一個沒有點.就是咱們的ip地址了
反義
上面已經提到過能夠用\d來表示數字, \b表示字符開頭或者結尾,\w表示數字字母或者漢字,那若是表示這些東西的反義怎麼辦?
那就是把他們[大寫], 好比 \W 匹配任意不是數字字母下劃線漢字的字符; \S 匹配任意不是空白符的字符 \D 匹配任意不是數字的字符 \B 匹配不是單詞開始或者結尾的位置 [^x] 匹配x之外的任意字符 [^aeiou] 匹配除了aeiou這幾個字符之外的全部字符
後向引用
這時候咱們須要匹配一些重複的內容,這個時候可能就須要用後後向引用了, 顧名思義,就是前面定義的正則, 後面直接經過一種方式來引用. 固然也不是任意匹配均可以引用,只能是小括號裏的表達式才能引用.具體表示以下:使用小括號指定一個子表達式後,匹配這個子表達式的文本(也就是此分組捕獲的內容)能夠在表達式或其它程序中做進一步的處理。 此處重點是:1.小括號的表達式才能被引用, 2. 匹配的是字表達式的文本,也就是內容,而不是繼續匹配.
每一個分組會自動擁有一個組號,規則是:從左向右,以分組的左括號爲標誌,第一個出現的分組的組號爲1,第二個爲2,以此類推。1.分組0對應整個正則表達式,2.實際上組號分配過程是要從左向右掃描兩遍的:第一遍只給未命名組分配,第二遍只給命名組分配--所以全部命名組的組號都大於未命名的組號3.你可使用(?:exp)這樣的語法來剝奪一個分組對組號分配的參與權.
\b(\w+)\b\s+\1\b 能夠用來匹配重複的單詞, 好比像go go, 或者kitty kitty。這個表達式首先是一個單詞,也就是單詞開始處和結束處之間的多於一個的字母或數字(\b(\w+)\b),這個單詞會被捕獲到編號爲1的分組中,而後是1個或幾個空白符(\s+),最後是分組1中捕獲的內容(也就是前面匹配的那個單詞)(\1)。
你也能夠本身指定子表達式的組名。要指定一個子表達式的組名,請使用這樣的語法:(?\w+)(或者把尖括號換成'也行:(?'Word'\w+)),這樣就把\w+的組名指定爲Word了。要反向引用這個分組捕獲的內容,你可使用\k,因此上一個例子也能夠寫成這樣:\b(?\w+)\b\s+\k\b
經常使用的分組
代碼/語法 |
說明 |
示例 |
(exp) |
匹配exp,並捕獲文本到自動命名的組裏 |
|
(?exp) |
匹配exp,並捕獲文本到名稱爲name的組裏,也能夠寫成(?'name'exp) |
|
?:exp) |
匹配exp,不捕獲匹配的文本,也不給此分組分配組號 |
|
(?=exp) |
匹配exp前面的位置 |
|
(?<=exp) |
匹配exp後面的位置 |
|
(?!exp) |
匹配後面跟的不是exp的位置 |
|
(?<!exp) |
匹配前面不是exp的位置 |
|
(?#comment) |
這種類型的分組不對正則表達式的處理產生任何影響,用於提供註釋讓人閱讀 |
|
零寬斷言
接下來的四個用於查找在某些內容(但並不包括這些內容)以前或以後的東西,也就是說它們像\b,^,$那樣用於指定一個位置,這個位置應該知足必定的條件(即斷言),所以它們也被稱爲零寬斷言。最好仍是拿例子來講明吧:
(?=exp)也叫零寬度正預測先行斷言,它斷言自身出現的位置的後面能匹配表達式exp。好比\b\w+(?=ing\b),匹配以ing結尾的單詞的前面部分(除了ing之外的部分),如查找I'm singing while you're dancing.時,它會匹配sing和danc。
(?<=exp)也叫零寬度正回顧後發斷言,它斷言自身出現的位置的前面能匹配表達式exp。好比(?<=\bre)\w+\b會匹配以re開頭的單詞的後半部分(除了re之外的部分),例如在查找reading a book時,它匹配ading。
負向零寬斷言
前面咱們提到過怎麼查找不是某個字符或不在某個字符類裏的字符的方法(反義)。可是若是咱們只是想要確保某個字符沒有出現,但並不想去匹配它時怎麼辦?例如,若是咱們想查找這樣的單詞--它裏面出現了字母q,可是q後面跟的不是字母u,咱們能夠嘗試這樣:
\b\w*q[^u]\w*\b匹配包含後面不是字母u的字母q的單詞。可是若是多作測試(或者你思惟足夠敏銳,直接就觀察出來了),你會發現,若是q出如今單詞的結尾的話,像Iraq,Benq,這個表達式就會出錯。這是由於[^u]總要匹配一個字符,因此若是q是單詞的最後一個字符的話,後面的[^u]將會匹配q後面的單詞分隔符(多是空格,或者是句號或其它的什麼),後面的\w*\b將會匹配下一個單詞,因而\b\w*q[^u]\w*\b就能匹配整個Iraq fighting。負向零寬斷言能解決這樣的問題,由於它只匹配一個位置,並不消費任何字符。如今,咱們能夠這樣來解決這個問題:\b\w*q(?!u)\w*\b。
零寬度負預測先行斷言(?!exp),斷言此位置的後面不能匹配表達式exp。例如:\d{3}(?!\d)匹配三位數字,並且這三位數字的後面不能是數字;\b((?!abc)\w)+\b匹配不包含連續字符串abc的單詞。 同理,咱們能夠用(?<!exp),零寬度負回顧後發斷言來斷言此位置的前面不能匹配表達式exp:(?<![a-z])\d{7}匹配前面不是小寫字母的七位數字。 請詳細分析表達式(?<=<(\w+)>).*(?=<\/\1>),這個表達式最能表現零寬斷言的真正用途。
一個更復雜的例子:(?<=<(\w+)>).*(?=<\/\1>)匹配不包含屬性的簡單HTML標籤內裏的內容。(?<=<(\w+)>)指定了這樣的前綴:被尖括號括起來的單詞(好比多是),而後是.*(任意的字符串),最後是一個後綴(?=<\/\1>)。注意後綴裏的\/,它用到了前面提過的字符轉義;\1則是一個反向引用,引用的正是捕獲的第一組,前面的(\w+)匹配的內容,這樣若是前綴其實是的話,後綴就是了。整個表達式匹配的是和之間的內容(再次提醒,不包括前綴和後綴自己)。
貪婪和懶惰
當正則表達式中包含可以接受重複的限定符時,一般的行爲是儘量多的匹配字符, 以a.*b爲例, 它會匹配以a開始,b結束的字符, 若是用來搜索aabab的話,它匹配整個字符,這叫作貪婪匹配.
有點時候咱們但願懶惰匹配,也就是匹配儘量少的字符.前面給出的限定符均可以被轉化爲懶惰匹配模式,只要在它後面加上一個問號?。這樣.*?就意味着匹配任意數量的重複,可是在能使整個匹配成功的前提下使用最少的重複。
.*?b匹配最短的,以a開始,以b結束的字符串。若是把它應用於aabab的話,它會匹配aab(第一到第三個字符)和ab(第四到第五個字符)。