正則表達(dá)式的干貨分享

一、基本概念:

在我們寫頁(yè)面時(shí),常需要對(duì)表單數(shù)據(jù)如賬號(hào)、密碼、身份證號(hào)等進(jìn)行驗(yàn)證,而最有效、用的最多的就是使用正則表達(dá)式來(lái)驗(yàn)證。

正則表達(dá)式(Regular Expression)是用于描述一組字符串特征的模式,用于匹配特定的字符串。其應(yīng)用非常廣泛,特別是在字符串處理方面,應(yīng)用如下:

1、驗(yàn)證字符串,即驗(yàn)證給定的字符串或子字符串是否符合指定的特征,可驗(yàn)證郵箱地址等。

2、查找字符串,從給定的文本中查找符號(hào)指定特征的字符串,比查找固定字符串更靈活。

3、替換字符串,即查找到符合某特征的字符串后將其替換。

4、提取字符串,即從給定的字符串中提取出符合指定特征的子字符串。

二、檢測(cè)工具介紹

介紹一個(gè)工具RegexBuddy

RegexBuddy

三、結(jié)構(gòu)總體介紹

1、字符字面量:

匹配一個(gè)具體字符,包括不用轉(zhuǎn)義的和需要轉(zhuǎn)義的。比如a匹配字符"a",又比如\n匹配換行符,又比如\.匹配小數(shù)點(diǎn)。

2、字符組:

匹配一個(gè)字符,可以是多種可能之一,比如[0-9],表示匹配一個(gè)數(shù)字。也有\d的簡(jiǎn)寫形式。另外還有反義字符組,表示可以是除了特定字符之外任何一個(gè)字符,比如[^0-9],表示一個(gè)非數(shù)字字符,也有\D的簡(jiǎn)寫形式。

3、量詞:

表示一個(gè)字符連續(xù)出現(xiàn),比如a{1,3}表示“a”字符連續(xù)出現(xiàn)1到3次。另外還有常見的簡(jiǎn)寫形式,比如a+表示“a”字符連續(xù)出現(xiàn)至少一次。

4、錨點(diǎn):

匹配一個(gè)位置,而不是字符。比如^匹配字符串的開頭,又比如\b匹配單詞邊界,又比如(?=\d)表示數(shù)字前面的位置。

5、分組:

用括號(hào)表示一個(gè)整體,比如(ab)+,表示"ab"兩個(gè)字符連續(xù)出現(xiàn)多次,也可以使用非捕獲分組(?:ab)+。

6、分支:

多個(gè)子表達(dá)式多選一,比如abc|bcd,表達(dá)式匹配"abc"或者"bcd"字符子串。

7、反向引用:

比如\2,表示引用第2個(gè)分組。

四、結(jié)構(gòu)詳細(xì)介紹

4.1 字符組

雖然是叫字符組,但只是表示其中一個(gè)字符,例如[abc],表示匹配一個(gè)字符,它可以是“a”、“b”、“c”之一。

(1)范圍表示法:

比如[123456abcdefGHIJKLM],可以寫成[1-6a-fG-M]。用連字符-來(lái)省略和簡(jiǎn)寫。

要匹配“a”、“-”、“z”這三者中任意一個(gè)字符,該怎么做呢?不能寫成[a-z],因?yàn)槠浔硎拘懽址械娜魏我粋€(gè)字符??梢詫懗扇缦碌姆绞剑?b>[-az]或[az-][a\-z]。即要么放在開頭,要么放在結(jié)尾,要么轉(zhuǎn)義??傊粫?huì)讓引擎認(rèn)為是范圍表示法就行。

(2)排除字符組:

某位字符可以是任何東西,但就不能是"a"、"b"、"c",排除字符組是反義字符組的概念。

例如[^abc],表示是一個(gè)除"a"、"b"、"c"之外的任意一個(gè)字符。字符組的第一位放^(脫字符),表示求反的概念。

(3)常見的簡(jiǎn)寫形式:

\d就是[0-9]。表示是一位數(shù)字。記憶方式:其英文是digit(數(shù)字)。

\D就是[^0-9]。表示除數(shù)字外的任意字符。

\w就是[0-9a-zA-Z_]。表示數(shù)字、大小寫字母和下劃線。記憶方式:w是word的簡(jiǎn)寫,也稱單詞字符。

\W是[^0-9a-zA-Z_]。非單詞字符。

\s是[ \t\v\n\r\f]。表示空白符,包括空格、水平制表符、垂直制表符、換行符、回車符、換頁(yè)符。記憶方式:s是space character的首字母。

\S是[^ \t\v\n\r\f]。 非空白符。

.就是[^\n\r\u2028\u2029]。通配符,表示幾乎任意字符。換行符、回車符、行分隔符和段分隔符除外。記憶方式:想想省略號(hào)...中的每個(gè)點(diǎn),都可以理解成占位符,表示任何類似的東西。

如果要匹配任意字符怎么辦?可以使用[\d\D]、[\w\W]、[\s\S]和[^]中任何的一個(gè)。

4.2 量詞

量詞也稱為重復(fù),表示重復(fù)多少次;例如:\d{3}表示連續(xù)匹配數(shù)字3次,即匹配三個(gè)連貫的數(shù)字。其中{}是閉區(qū)間。

(1)簡(jiǎn)寫形式

{m,} 表示至少出現(xiàn)m次。

{m} 等價(jià)于{m,m},表示出現(xiàn)m次。

? 等價(jià)于{0,1},表示出現(xiàn)或者不出現(xiàn)。記憶方式:?jiǎn)柼?hào)表示,有或沒有

+ 等價(jià)于{1,},表示出現(xiàn)至少一次。

* 等價(jià)于{0,},表示出現(xiàn)任意次,有可能不出現(xiàn)。

(2)量詞中存在的貪婪匹配和惰性匹配

例如:

貪婪匹配

其中正則\d{2,5},表示數(shù)字連續(xù)出現(xiàn)2到5次。會(huì)匹配2位、3位、4位、5位連續(xù)數(shù)字。但是其是貪婪的,它會(huì)盡可能多的匹配。你能給我6個(gè),我就要5個(gè)。你能給我3個(gè),我就要3個(gè)。反正只要在能力范圍內(nèi),越多越好。

而惰性匹配,就是盡可能少的匹配;

惰性匹配

其中\d{2,5}?表示,雖然2到5次都行,當(dāng)2個(gè)就夠的時(shí)候,就不在往下嘗試了。

4.3 錨點(diǎn)

錨點(diǎn)即位置匹配,介紹錨點(diǎn)之前先了解下位置的概念。

(1)什么是位置

位置是相鄰字符之間的位置。

位置的概念

(2)如何匹配位置

有8個(gè)錨字符可以去匹配位置

^? ? $? ? \b? ? \B? ? (?=p)? ? (?!p)??(?<=p) (?<!p)

其中

^和$

^(脫字符)匹配開頭,在多行匹配中匹配行開頭。$(美元符號(hào))匹配結(jié)尾,在多行匹配中匹配行結(jié)尾。比如,把字符串的開頭和結(jié)尾用“#”替換。

開頭和結(jié)尾用#替換

\b和\B

\b是單詞邊界,具體就是\w\W之間的位置,也包括\w^之間的位置,也包括\w$之間的位置。

例如,將指定字符的邊界替換為#

替換邊界

\B就是\b的反面的意思,非單詞邊界。例如在字符串中所有位置中,扣掉\b,剩下的都是\B的。

非單詞邊界

(?=p) 和 (?!p)

(?=p),學(xué)名叫零寬度預(yù)測(cè)先行斷言,其中p是一個(gè)子模式,即p前面的位置。例如(?=l),表示'l'字符前面的位置

將'l'前面的位置替換為‘#’

(?!p),學(xué)名叫零寬度負(fù)預(yù)測(cè)先行斷言,就是(?=p)的反面意思,比如:

將不是‘l’前面的位置替換為‘#’

(3)案例分析

數(shù)字的千位分隔符表示法,如把“12345678”,變成“12,345,678”。

思路:找到對(duì)應(yīng)的位置替換為“,”。

1、先弄出最后一個(gè)逗號(hào)

將后面三位數(shù)字的前面的位置替換為','

2、弄出所有逗號(hào)

因?yàn)槎禾?hào)出現(xiàn)的位置,要求后面3個(gè)數(shù)字一組,也就是\d{3}至少出現(xiàn)一次。此時(shí)可以使用量詞+:

開頭部分也被匹配進(jìn)去了

3、要求匹配的位置不能是開頭

使用(?!^)排除開頭位置
使用\B也能實(shí)現(xiàn)同樣效果

(?<=p) 和(?<!p)

注:這兩種模式j(luò)avascript是不支持的,請(qǐng)?jiān)赗egexBuddy工具中把語(yǔ)言切換到c#模式。

(?<=p) ,學(xué)名叫零寬度回顧后發(fā)斷言,表示p后面的這個(gè)位置

(?<=p)表示p后面的位置

(?<!p),學(xué)名叫零寬度負(fù)回顧后發(fā)斷言,表示不是p后面的位置,或者說(shuō)是p后面的位置不能匹配;

不能匹配p的后面位置的內(nèi)容


4.4 多選分支

形式如:(p1|p2|p3),其中p1、p2、p3是子模式,用|(管道符)分隔,表示其中任何之一。

它也是惰性匹配,即找到一個(gè)匹配的就不繼續(xù)嘗試查找了。

4.5 括號(hào)的作用

a+匹配連續(xù)出現(xiàn)的“a”,而要匹配連續(xù)出現(xiàn)的“ab”時(shí),需要使用(ab)+

(ab)+的匹配

多選分支結(jié)構(gòu)(p1|p2)中,括號(hào)提供了子表達(dá)式的所有可能。如:

兩種都能匹配

反向引用

在正則里引用之前出現(xiàn)的分組,即反向引用。如:當(dāng)我們想用正則支持如下三種日期格式時(shí):

匹配三種格式的日期

以上正則雖然能匹配要求的情況,但也匹配了“2019-09/10”這樣的數(shù)據(jù),如果想要分割符前后一致,需要使用反向引用;

反向引用

其中,\1表示引用之前的那個(gè)分組(-|\/|\.)。不管它匹配到什么(比如-),\1都匹配那個(gè)同樣的具體某個(gè)字符。我們知道了\1的含義后,那么\2\3的概念也就理解了,即分別指代第二個(gè)和第三個(gè)分組。

當(dāng)括號(hào)存在嵌套時(shí),以左括號(hào)為準(zhǔn),如

括號(hào)嵌套

我們可以看看這個(gè)正則匹配模式:

第一個(gè)字符是數(shù)字,比如說(shuō)1,

第二個(gè)字符是數(shù)字,比如說(shuō)2,

第三個(gè)字符是數(shù)字,比如說(shuō)3,

接下來(lái)的是\1,是第一個(gè)分組內(nèi)容,那么看第一個(gè)開括號(hào)對(duì)應(yīng)的分組是什么,是123,

接下來(lái)的是\2,找到第2個(gè)開括號(hào),對(duì)應(yīng)的分組,匹配的內(nèi)容是1,

接下來(lái)的是\3,找到第3個(gè)開括號(hào),對(duì)應(yīng)的分組,匹配的內(nèi)容是23,

最后的是\4,找到第4個(gè)開括號(hào),對(duì)應(yīng)的分組,匹配的內(nèi)容是3。

引用不存在的分組

反向引用,是引用前面的分組,但我們在正則里引用了不存在的分組時(shí),有些語(yǔ)言會(huì)報(bào)編譯錯(cuò)誤(引用了不存在的分組),如c#,但有些語(yǔ)言是支持的,如javascript,只是匹配反向引用的字符本身。例如\2,就匹配"\2"。注意"\2"表示對(duì)"2"進(jìn)行了轉(zhuǎn)意。大概如:

\1到\9代表的具體字符

非捕獲分組

之前文中出現(xiàn)的分組,都會(huì)捕獲它們匹配到的數(shù)據(jù),以便后續(xù)引用,因此也稱他們是捕獲型分組。如果只要括號(hào)最原始的功能,但不會(huì)引用它,即,既不在API里引用,也不在正則里反向引用。此時(shí)可以使用非捕獲分組(?:p),注意和上文中的(?=p)進(jìn)行區(qū)分。

非捕獲分組

4.6 回溯法原理

引擎會(huì)依次處理各個(gè)子表達(dá)式或組成元素,遇到需要在兩個(gè)可能成功的選項(xiàng)進(jìn)行選擇的時(shí)候,它會(huì)選擇其一,同時(shí)記住另一個(gè),以備稍后可能的需要。如果正則表達(dá)式中余下的部分最終匹配失敗,引擎會(huì)知道需要回溯到之前做出選擇的地方,選擇其他的備用分支繼續(xù)嘗試。這樣,引擎最終會(huì)嘗試表達(dá)式的所有可能途徑(或者是匹配完成之前需要的所有途徑)。

形象的說(shuō),回溯就像是在道路的每個(gè)分岔口留下一小堆面包屑。如果走了死路,就可以照原路返回,直到遇到面包屑標(biāo)示的尚未嘗試過的道路。如果那條路也走不通,你可以繼續(xù)返回,找到下一堆面包屑,如此重復(fù),直到找到出路,或者走完所有沒有嘗試過的路。

(1)沒有回溯的匹配

假設(shè)我們的正則是ab{1,3}c,其可視化形式是

可視化形式

當(dāng)目標(biāo)字符串是"abbbc"時(shí),就沒有所謂的“回溯”。其匹配過程是

沒有回溯的匹配

(2)有回溯的匹配

如果目標(biāo)字符串是“abbc”,則會(huì)存在回溯。

有回溯的匹配

圖中第5步有紅顏色,表示匹配不成功。此時(shí)b{1,3}已經(jīng)匹配到了2個(gè)字符“b”,準(zhǔn)備嘗試第三個(gè)時(shí),結(jié)果發(fā)現(xiàn)接下來(lái)的字符是“c”。那么就認(rèn)為b{1,3}就已經(jīng)匹配完畢。然后狀態(tài)又回到之前的狀態(tài)(即第6步,與第4步一樣),最后再用子表達(dá)式c,去匹配字符“c”。當(dāng)然,此時(shí)整個(gè)表達(dá)式匹配成功了。其中的第6步就是回溯

再舉個(gè)例子,正則是ab{1,3}bbc

可視化形式

目標(biāo)字符串是"abbbc",匹配過程是:

有回溯的匹配

其中第7步和第10步是回溯。第7步與第4步一樣,此時(shí)b{1,3}匹配了兩個(gè)"b",而第10步與第3步一樣,此時(shí)b{1,3}只匹配了一個(gè)"b",這也是b{1,3}的最終匹配結(jié)果。

(3)回溯法總結(jié)

回溯法也稱試探法,它的基本思想是:從問題的某一種狀態(tài)(初始狀態(tài))出發(fā),搜索從這種狀態(tài)出發(fā)所能達(dá)到的所有“狀態(tài)”,當(dāng)一條路走到“盡頭”的時(shí)候(不能再前進(jìn)),再后退一步或若干

步,從另一種可能“狀態(tài)”出發(fā),繼續(xù)搜索,直到所有的“路徑”狀態(tài)都嘗試過。這種不斷“前進(jìn)”、不斷“回溯”尋找解的方法,就稱為“回溯法”。

(4)可能出現(xiàn)回溯的地方

貪婪量詞

之前的例子都是貪婪量詞相關(guān)的。比如b{1,3},因?yàn)槠涫秦澙返模瑖L試可能的順序是從多往少的方向去嘗試。首先會(huì)嘗試"bbb",然后再看整個(gè)正則是否能匹配。不能匹配時(shí),吐出一個(gè)"b",即在"bb"的基礎(chǔ)上,再繼續(xù)嘗試。如果還不行,再吐出一個(gè),再試。如果還不行呢?只能說(shuō)明匹配失敗了。

惰性量詞

惰性量詞就是在貪婪量詞后面加個(gè)問號(hào),表示盡可能少的匹配。雖然惰性量詞不貪,但也會(huì)有回溯的現(xiàn)象。比如正則是

惰性量詞的回溯
有回溯的匹配

雖然惰性匹配不貪,但是為了整體匹配成,也只能給你多塞點(diǎn)。因此最后\d{1,3}?匹配的字符是"12",是兩個(gè)數(shù)字,而不是一個(gè)。

五、實(shí)踐

(1)試著用正則表達(dá)式匹配IP地址;

(2)驗(yàn)證用戶密碼,要求必須是大、小寫字母、數(shù)字至少兩種的結(jié)合;

(3)了解下正則表達(dá)式引發(fā)的血案,你知道問題出在哪兒?jiǎn)幔? ?https://juejin.im/post/5b287ea6f265da596d04a324

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 正則表達(dá)式是匹配模式,匹配字符或者匹配位置。 一、字符匹配 1.兩種模糊匹配 1.1 橫向模糊匹配 一個(gè)正則可匹配...
    菜菜的小阿允閱讀 1,731評(píng)論 0 0
  • 正則表達(dá)式學(xué)習(xí)筆記 一篇記錄了學(xué)習(xí)正則表達(dá)式的筆記。 1. 轉(zhuǎn)義字符 在 HTMl 中轉(zhuǎn)義字符以 & 符號(hào)開頭,分...
    heyi_let閱讀 480評(píng)論 0 0
  • Python中的正則表達(dá)式(re) import rere.match #從開始位置開始匹配,如果開頭沒有則無(wú)re...
    BigJeffWang閱讀 7,577評(píng)論 0 99
  • 正則表達(dá)式有很多流派,也有很多的特性,不同的語(yǔ)言支持度也是不一樣的。本篇文章是寫Python中的正則表達(dá)式的用法的...
    Moscow1147閱讀 1,201評(píng)論 0 0
  • 從匹配中返回值 Match 對(duì)象 成功的匹配總是返回一個(gè) Match 對(duì)象, 這個(gè)對(duì)象通常也被放進(jìn) $/ 中, (...
    焉知非魚閱讀 1,936評(píng)論 0 1

友情鏈接更多精彩內(nèi)容