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