这样学习正则表达式就轻松了!
在日常工作中,經常會用到正則操作。但是對于大多數人來說,操作正則表達式簡直就是抓瞎。
本篇文章主要整理了正則表達式匹配的規則,使用中的一些要點,以及用圖形化的方式列舉出一些常見的正則表達式,希望能給大家帶來一定的幫助,能在以后的工作中,用上正則,愛上正則。
PS:不同語言中的正則表達式的規則不完全相同,但是大部分都可以適用。
正則是什么
正則表達式是為了對字符串進行有效 數據提取 以及 匹配 的一種機制,字符串在匹配的過程中將會從第一個位置開始匹配,然后從左往右進行依次匹配,每嘗試匹配一次,就會把控制權交由下一個位置,直到匹配結束。
正則表達式是由 普通字符(例如字符 a 到 z)以及 特殊字符(稱為元字符)組成的文字模式。該模式描述在查找文字主體時待匹配的一個或多個字符串。正則表達式作為一個模板,將某個字符模式與所搜索的字符串進行匹配。
正則的誕生
正則表達式的“祖先”可以一直上溯至對人類神經系統如何工作的早期研究。Warren McCulloch 和 Walter Pitts 這兩位神經生理學家研究出一種數學方式來描述這些神經網絡。
1956 年, 一位叫 Stephen Kleene 的美國數學家在 McCulloch 和 Pitts 早期工作的基礎上,發表了一篇標題為「神經網事件的表示法」的論文,引入了正則表達式的概念。
正則表達式就是用來描述他稱為“正則集的代數”的表達式,因此采用“正則表達式”這個術語。
隨后,人們發現可以將這一工作應用于使用Ken Thompson 的計算搜索算法的一些早期研究,Ken Thompson是Unix 的主要發明人。正則表達式的第一個實用應用程序就是 Unix 中的qed 編輯器。從那時起直至現在正則表達式都是基于文本的編輯器和搜索工具中的一個重要部分。具有完整語法的正則表達式使用在字符的格式匹配方面上,后來被應用到熔融信息技術領域。自從那時起,正則表達式經過幾個時期的發展,現在的標準已經被ISO(國際標準組織)批準和被Open Group組織認定。
匹配規則
下面將正則中的一些基本的匹配規則列出來如下表所示:
要點
貪與不貪
舉個例子,假設有以下這段html字符,我想拿到a標簽中的內容:
<a>南京長江大橋</a>哈哈<a>南京市長江大橋</a>
然后我寫了這樣一個正則: <a>(.)*</a>
在線測試的結果如下:
這個結果與我們的預期不符,正常我應該得到兩個匹配的結果才對,但是現在卻只匹配到一個結果。
現在把剛剛的正則改成這樣: <a>(.)*?</a>
在線測試的結果如下:
貪 說的是正則在不約束的情況下會繼續自動向右進行匹配,直到匹配結束,只要匹配的數據與正則的最后一個值匹配就算是匹配到了。
不貪 說的是只要匹配到就結束,不繼續向右進行匹配了。
問號 ? 就解決了貪婪的問題,使得問號前面的字符匹配到之后就結束,但是并不是把 ? 放在哪里都可以解決貪婪的,在正則里,有一些屬于貪婪模式量詞,比如以下這些:
{m,n} {m,} ? * +斷言與零寬
在java中我們知道 斷言 可以用來聲明一個應該為 true 的事實,只有當斷言為真時才會繼續進行后續的操作。
在正則中也有 斷言 的概念,但是在正則中除了 斷言 還有 零寬 的概念。
-
斷言:
通俗點將斷言就是 “我斷定某某情況是真的” ,而正則中的斷言,就是說正則可以斷定在 指定的內容 的 前面 或 后面 會出現滿足指定規則的內容。比如 "aa1bb2cc3",正則可以用斷言找出 bb2 前面有 aa1,也可以找出 bb2 后面有 cc3。
-
零寬:
零寬就是沒有寬度,在正則中,斷言只是匹配位置,不占字符,也就是說,匹配結果里是不會返回斷言本身的。
斷言一共有四種情況:
讓我們來舉個例子來說明吧,假設我們現在拿到了某個網頁的html,里面有個閱讀數的標簽:
span class="read-cnt">閱讀數:1024</span>
現在我們要獲取到這個閱讀數,該怎么辦呢?
如果用正向先行斷言來匹配的話,可以這樣來寫:
\d+(?=</span>)
上述的表達式就是說明,我現在斷言整數 \d+ 的 后面 能 匹配表達式: </span>
讓我們來驗證下結果:
相應的正向后行斷言可以這樣寫表達式:
(?<=閱讀數:)\d+
上述的表達式就是說明,我現在斷言整數 \d+ 的 前面 能 匹配表達式: 閱讀數:
驗證下結果如下:
分組
正則表達式中用小括號 () 來做分組,也就是括號中的內容作為一個整體。
因此當我們要匹配分組 he 的時候,可以用下面這個表達式 :
(he)
我們看到正則表達式用小括號來做分組,那么問題來了:
如果要匹配的字符串中本身就包含小括號,那應該怎么辦?
針對這種情況,正則提供了轉義的方式,也就是要把這些元字符、限定符或者關鍵字轉義成普通的字符,做法很簡單,就是在要轉義的字符前面加個斜杠(\)即可。
因此當我們要匹配分組 (he) 的時候,可以用下面這個表達式 :
(\(he\))
下面我們用一個正則表達式的圖形生成工具,做一個對比的實驗,讓我們對分組和定位有個了解。
1:匹配 he 分組一次 ;
2:匹配 he 分組零或多次;
3:匹配以 he 開頭的分組一次;
4:匹配以 he 開頭的分組零或多次
捕獲與反向引用
單純說到捕獲,他的意思是匹配表達式,但捕獲通常和分組聯系在一起,也就是“捕獲組”。
捕獲組:
匹配子表達式的內容,把匹配結果保存到內存中以數字編號或顯示命名的組里(可以把它想象為java中的array和map),以深度優先進行編號,之后可以通過序號或名稱來使用這些匹配結果。
捕獲組的表達式為: (exp) ,這個語法跟上面講到的分組的概念是一樣的,只是捕獲將匹配到的分組,保存在了內存中,留待后面使用。具體怎么時候他不管,他只需要把匹配到的分組保存在內存中就可以了。
有一種情況當在匹配的過程中,需要與已經捕獲到的分組進行匹配,這時就需要使用到保存在內存中的捕獲組了,這種使用方式就被稱為: 反向引用 。
假設我有這樣一段文字:
aa12bb23cc34
現在我想拿到成對的字符,該怎么做呢?這種情況下通過斷言或者其他方式是辦不到的,那我們能否在匹配的過程中將匹配到的一個字符先保存在內存中,然后匹配下一個字符時再與上一個字符相比較,如果相等,就說明匹配成了,拿到了成對的字符了。
那首先我們先要寫一個匹配單個字符分組的表達式:
(\w)
那當匹配時捕獲到一個字符分組時,我們需要將該字符引用出來,與下一個字符想比較,我們期望匹配的下一個字符也與我當前保存的字符相等,那么表達式就變成了這樣:
(\w)\1
這里的 \1 表示的是,當前正則表達式匹配到的 第1個 分組,那就意味著, \2 表示 第2個分組。
做個測試,結果如下:
那如果我想再匹配復雜一點的結果,比如:XYY 這種的結果,又該怎么寫呢?
其實有了上面的基礎之后就很簡單了,我們需要做的就是 對捕獲到的第2個分組進行反向引用就可以了!
具體的表達式為:
(\w)(\w)\2
測試結果如下:
表示成圖形就是這樣:
常見正則
為了更加形象的了解正則表達式,我們最后通過圖形的方式來了解一些常見的正則表達式,使用圖形的目的是希望能對冷冰冰的表達式有個更深刻的認識。
以下是一些常見正則的表達式與圖片,可能有些過時了,如電話號碼出現了新的號段,但是大體上應該沒有問題。
整數
[0-9]+
逗號分隔的整數
\b[0-9]{1,3}(,[0-9]{3})*\b
浮點數
(\+?(\d+|\.\d+|\d+\.\d+)|-?(\d+|\d+\.\d+))
0-255之間的數字
^([0-9]|[0-9]{2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])$
身份證
^[1-9]\d{14}(\d{2}[0-9x])?$
郵箱
^[-\w.]{0,64}@([a-zA-Z0-9]{1,63}\.)*[-a-zA-Z0-9]{1,63}$
固定電話
(\(?0[1-9]{2,3}\)?-?)?[1-9][0-9]\{6,7}(-[0-9]{1,6})?
郵編
[1-9][0-9]{5}
ISBN
((ISBN(-13)?:?\s)?97[89][-\s]?[0-9][-\s]?[0-9]{3}[-\s]?[0-9]{5}[-\s]?[0-9]|(ISBN(-10)?:?\s)?[0-9][-\s]?[0-9]{3}[-\s]?[0-9]{5}[-\s]?[0-9x])
手機號
(0|\+86)?(13[0-9]|15[0-356]|18[025-9])\d{8}
成對的html標簽
如 test
<([^>]+)>[\s\S]*?<\/\1>
a標簽
<a\s+href\s*=\s*["']?([^"'\s]+)["']?>([^<]+)<\/a>
head標簽
<head>([^>]+)<\/head>
image標簽
<img\s[^>]*?src=['"]?([^"']+)["']?[^>]*>
正則思維導圖
附常用工具:
在線正則測試:http://tool.oschina.net/regex/
生成正則圖片:https://regexper.com
總結
以上是生活随笔為你收集整理的这样学习正则表达式就轻松了!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我作为开发者犯过的两次愚蠢的错误
- 下一篇: 一文读懂什么是Java中的自动拆装箱