日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

正则表达式不用背

發(fā)布時(shí)間:2025/3/19 编程问答 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 正则表达式不用背 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

  

正則表達(dá)式一直是困擾很多程序員的一門技術(shù),當(dāng)然也包括曾經(jīng)的我。大多數(shù)時(shí)候我們?cè)陂_發(fā)過程中要用到某些正則表達(dá)式的時(shí)候,都會(huì)打開谷歌或百度直接搜索然后拷貝粘貼。當(dāng)下一次再遇到相同問題的時(shí)候,同樣的場(chǎng)景又再來一遍。作為一門用途很廣的技術(shù),我相信深入理解正則表達(dá)式并能融會(huì)貫通是值得的。所以,希望這篇文章能幫助大家理清思路,搞懂正則表達(dá)式各種符號(hào)之間的內(nèi)在聯(lián)系,形成知識(shí)體系,當(dāng)下次再遇到正則表達(dá)式的時(shí)候可以不借助搜索引擎,自己解決。

正則表達(dá)式到底是什么

  正則表達(dá)式(Regular Expression)其實(shí)就是一門工具,目的是為了字符串模式匹配,從而實(shí)現(xiàn)搜索和替換功能。它起源于上個(gè)20世紀(jì)50年代科學(xué)家在數(shù)學(xué)領(lǐng)域做的一些研究工作,后來才被引入到計(jì)算機(jī)領(lǐng)域中。從它的命名我們可以知道,它是一種用來描述規(guī)則的表達(dá)式。而它的底層原理也十分簡(jiǎn)單,就是使用狀態(tài)機(jī)的思想進(jìn)行模式匹配。大家可以利用https://regexper.com這個(gè)工具很好地可視化自己寫的正則表達(dá)式:

如/\d\w+/這個(gè)正則生成的狀態(tài)機(jī)圖:

  對(duì)于具體的算法實(shí)現(xiàn),大家如果感興趣可以閱讀《算法導(dǎo)論》。

從字符出發(fā)

  我們學(xué)習(xí)一個(gè)系統(tǒng)化的知識(shí),一定要從其基礎(chǔ)構(gòu)成來了解。正則表達(dá)式的基本組成元素可以分為:字符和元字符。字符很好理解,就是基礎(chǔ)的計(jì)算機(jī)字符編碼,通常正則表達(dá)式里面使用的就是數(shù)字、英文字母。而元字符,也被稱為特殊字符,是一些用來表示特殊語義的字符。如^表示非,|表示或等。利用這些元字符,才能構(gòu)造出強(qiáng)大的表達(dá)式模式(pattern)。接下來,我們就來從這些基本單位出發(fā),來學(xué)習(xí)一下如何構(gòu)建正則表達(dá)式。

單個(gè)字符

  最簡(jiǎn)單的正則表達(dá)式可以由簡(jiǎn)單的數(shù)字和字母組成,沒有特殊的語義,純粹就是一一對(duì)應(yīng)的關(guān)系。如想在'apple'這個(gè)單詞里找到‘a(chǎn)'這個(gè)字符,就直接用/a/這個(gè)正則就可以了。

但是如果想要匹配特殊字符的話,就得請(qǐng)出我們第一個(gè)元字符\, 它是轉(zhuǎn)義字符字符,顧名思義,就是讓其后續(xù)的字符失去其本來的含義。舉個(gè)例子:

我想匹配*這個(gè)符號(hào),由于*這個(gè)符號(hào)本身是個(gè)特殊字符,所以我要利用轉(zhuǎn)義元字符\來讓它失去其本來的含義:

/\*/

如果本來這個(gè)字符不是特殊字符,使用轉(zhuǎn)義符號(hào)就會(huì)讓它擁有特殊的含義。我們常常需要匹配一些特殊字符,比如空格,制表符,回車,換行等, 而這些就需要我們使用轉(zhuǎn)義字符來匹配。為了便于記憶,我整理了下面這個(gè)表格,并附上記憶方式:

特殊字符正則表達(dá)式記憶方式
換行符\nnew line
換頁符\fform feed
回車符\rreturn
空白符\sspace
制表符\ttab
垂直制表符\vvertical tab
回退符[\b]backspace,之所以使用[]符號(hào)是避免和\b重復(fù)

多個(gè)字符

  單個(gè)字符的映射關(guān)系是一對(duì)一的,即正則表達(dá)式的被用來篩選匹配的字符只有一個(gè)。而這顯然是不夠的,只要引入集合區(qū)間和通配符的方式就可以實(shí)現(xiàn)一對(duì)多的匹配了。

在正則表達(dá)式里,集合的定義方式是使用中括號(hào)[和]。如/[123]/這個(gè)正則就能同時(shí)匹配1,2,3三個(gè)字符。那如果我想匹配所有的數(shù)字怎么辦呢?從0寫到9顯然太過低效,所以元字符-就可以用來表示區(qū)間范圍,利用/[0-9]/就能匹配所有的數(shù)字,?/[a-z]/則可以匹配所有的英文小寫字母。

即便有了集合和區(qū)間的定義方式,如果要同時(shí)匹配多個(gè)字符也還是要一一列舉,這是低效的。所以在正則表達(dá)式里衍生了一批用來同時(shí)匹配多個(gè)字符的簡(jiǎn)便正則表達(dá)式:

匹配區(qū)間正則表達(dá)式記憶方式
除了換行符之外的任何字符.句號(hào),除了句子結(jié)束符
單個(gè)數(shù)字, [0-9]\ddigit
除了[0-9]\Dnot?digit
包括下劃線在內(nèi)的單個(gè)字符,[A-Za-z0-9_]\wword
非單字字符\Wnot?word
匹配空白字符,包括空格、制表符、換頁符和換行符\sspace
匹配非空白字符\Snot?space

循環(huán)與重復(fù)

  一對(duì)一和一對(duì)多的字符匹配都講完了。接下來,就該介紹如何同時(shí)匹配多個(gè)字符。要實(shí)現(xiàn)多個(gè)字符的匹配我們只要多次循環(huán),重復(fù)使用我們的之前的正則規(guī)則就可以了。那么根據(jù)循環(huán)次數(shù)的多與少,我們可以分為0次,1次,多次,特定次。

0 | 1

元字符?代表了匹配一個(gè)字符或0個(gè)字符。設(shè)想一下,如果你要匹配color和colour這兩個(gè)單詞,就需要同時(shí)保證u這個(gè)字符是否出現(xiàn)都能被匹配到。所以你的正則表達(dá)式應(yīng)該是這樣的:/colo?r/。

>= 0

元字符*用來表示匹配0個(gè)字符或無數(shù)個(gè)字符。通常用來過濾某些可有可無的字符串。

>= 1

元字符+適用于要匹配同個(gè)字符出現(xiàn)1次或多次的情況。

特定次數(shù)

  在某些情況下,我們需要匹配特定的重復(fù)次數(shù),元字符{和}用來給重復(fù)匹配設(shè)置精確的區(qū)間范圍。如'a'我想匹配3次,那么我就使用/a{3}/這個(gè)正則,或者說'a'我想匹配至少兩次就是用/a{2,}/這個(gè)正則。

以下是完整的語法:

- {x}: x次- {min, max}: 介于min次到max次之間- {min, }: 至少min次- {, max}: 至多max

由于這些元字符比較抽象,且容易混淆,所以我用了聯(lián)想記憶的方式編了口訣能保證在用到的時(shí)候就能回憶起來。

匹配規(guī)則元字符聯(lián)想方式
0次或1次?且問,此事有還無
0次或無數(shù)次*宇宙洪荒,辰宿列張:宇宙伊始,從無到有,最后星宿布滿星空
1次或無數(shù)次+一加, +1
特定次數(shù){x}, {min, max}可以想象成一個(gè)數(shù)軸,從一個(gè)點(diǎn),到一個(gè)射線再到線段。min和max分別表示了左閉右閉區(qū)間的左界和右界

位置邊界

上面我們把字符的匹配都介紹完了,接著我們還需要位置邊界的匹配。在長文本字符串查找過程中,我們常常需要限制查詢的位置。比如我只想在單詞的開頭結(jié)尾查找。

單詞邊界

單詞是構(gòu)成句子和文章的基本單位,一個(gè)常見的使用場(chǎng)景是把文章或句子中的特定單詞找出來。如:

The cat scattered his food all over the room.

我想找到cat這個(gè)單詞,但是如果只是使用/cat/這個(gè)正則,就會(huì)同時(shí)匹配到cat和scattered這兩處文本。這時(shí)候我們就需要使用邊界正則表達(dá)式\b,其中b是boundary的首字母。在正則引擎里它其實(shí)匹配的是能構(gòu)成單詞的字符(\w)和不能構(gòu)成單詞的字符(\W)中間的那個(gè)位置。

上面的例子改寫成/\bcat\b/這樣就能匹配到cat這個(gè)單詞了。

字符串邊界

匹配完單詞,我們?cè)賮砜匆幌乱徽麄€(gè)字符串的邊界怎么匹配。元字符^用來匹配字符串的開頭。而元字符$用來匹配字符串的末尾。注意的是在長文本里,如果要排除換行符的干擾,我們要使用多行模式。試著匹配I am scq000這個(gè)句子:

I am scq000. I am scq000. I am scq000.

我們可以使用/^I am scq000.$/m這樣的正則表達(dá)式,其實(shí)m是multiple line的首字母。正則里面的模式除了m外比較常用的還有i和g。前者的意思是忽略大小寫,后者的意思是找到所有符合的匹配。

最后,總結(jié)一下:

邊界和標(biāo)志正則表達(dá)式記憶方式
單詞邊界\bboundary
非單詞邊界\Bnot?boundary
字符串開頭^小頭尖尖那么大個(gè)
字符串結(jié)尾$終結(jié)者,美國科幻電影,美元符$
多行模式m標(biāo)志multiple of lines
忽略大小寫i標(biāo)志ignore case, case-insensitive
全局模式g標(biāo)志global

子表達(dá)式

  字符匹配我們介紹的差不多了,更加高級(jí)的用法就得用到子表達(dá)式了。通過嵌套遞歸和自身引用可以讓正則發(fā)揮更強(qiáng)大的功能。

從簡(jiǎn)單到復(fù)雜的正則表達(dá)式演變通常要采用分組、回溯引用和邏輯處理的思想。利用這三種規(guī)則,可以推演出無限復(fù)雜的正則表達(dá)式。

分組

  其中分組體現(xiàn)在:所有以(和)元字符所包含的正則表達(dá)式被分為一組,每一個(gè)分組都是一個(gè)子表達(dá)式,它也是構(gòu)成高級(jí)正則表達(dá)式的基礎(chǔ)。如果只是使用簡(jiǎn)單的(regex)匹配語法本質(zhì)上和不分組是一樣的,如果要發(fā)揮它強(qiáng)大的作用,往往要結(jié)合回溯引用的方式。

回溯引用

  所謂回溯引用(backreference)指的是模式的后面部分引用前面已經(jīng)匹配到的子字符串。你可以把它想象成是變量,回溯引用的語法像\1,\2,....,其中\(zhòng)1表示引用的第一個(gè)子表達(dá)式,\2表示引用的第二個(gè)子表達(dá)式,以此類推。而\0則表示整個(gè)表達(dá)式。

假設(shè)現(xiàn)在要在下面這個(gè)文本里匹配兩個(gè)連續(xù)相同的單詞,你要怎么做呢?

Hello what what is the first thing, and I am am scq000.

利用回溯引用,我們可以很容易地寫出\b(\w+)\s\1這樣的正則。

回溯引用在替換字符串中十分常用,語法上有些許區(qū)別,用$1,$2...來引用要被替換的字符串。下面以js代碼作演示:

var str = 'abc abc 123'; str.replace(/(ab)c/g,'$1g'); // 得到結(jié)果 'abg abg 123'

如果我們不想子表達(dá)式被引用,可以使用非捕獲正則(?:regex)這樣就可以避免浪費(fèi)內(nèi)存。

var str = 'scq000'. str.replace(/(scq00)(?:0)/, '$1,$2') // 返回scq00,$2 // 由于使用了非捕獲正則,所以第二個(gè)引用沒有值,這里直接替換為$2

有時(shí),我們需要限制回溯引用的適用范圍。那么通過前向查找和后向查找就可以達(dá)到這個(gè)目的。

前向查找

  前向查找(lookahead)是用來限制后綴的。凡是以(?=regex)包含的子表達(dá)式在匹配過程中都會(huì)用來限制前面的表達(dá)式的匹配。例如happy happily這兩個(gè)單詞,我想獲得以happ開頭的副詞,那么就可以使用happ(?=ily)來匹配。如果我想過濾所有以happ開頭的副詞,那么也可以采用負(fù)前向查找的正則happ(?!ily),就會(huì)匹配到happy單詞的happ前綴。

后向查找

  介紹完前向查找,接著我們?cè)賮斫榻B一下它的反向操作:后向查找(lookbehind)。后向查找(lookbehind)是通過指定一個(gè)子表達(dá)式,然后從符合這個(gè)子表達(dá)式的位置出發(fā)開始查找符合規(guī)則的字串。舉個(gè)簡(jiǎn)單的例子:?apple和people都包含ple這個(gè)后綴,那么如果我只想找到apple的ple,該怎么做呢?我們可以通過限制app這個(gè)前綴,就能唯一確定ple這個(gè)單詞了。

/(?<=app)ple/

其中(?<=regex)的語法就是我們這里要介紹的后向查找。regex指代的子表達(dá)式會(huì)作為限制項(xiàng)進(jìn)行匹配,匹配到這個(gè)子表達(dá)式后,就會(huì)繼續(xù)向后查找。另外一種限制匹配是利用(?<!regex)?語法,這里稱為負(fù)后向查找。與正前向查找不同的是,被指定的子表達(dá)式不能被匹配到。于是,在上面的例子中,如果想要查找apple的ple也可以這么寫成/(?<!peo)ple。

需要注意的,不是每種正則實(shí)現(xiàn)都支持后向查找。在javascript中是不支持的,所以如果有用到后向查找的情況,有一個(gè)思路是將字符串進(jìn)行翻轉(zhuǎn),然后再使用前向查找,作完處理后再翻轉(zhuǎn)回來。看一個(gè)簡(jiǎn)單的例子:

// 比如我想替換apple的ple為ply var str = 'apple people'; str.split('').reverse().join('').replace(/elp(?=pa)/, 'ylp').split('').reverse().join('');

最后回顧一下這部分內(nèi)容:

回溯查找正則記憶方式
引用\0,\1,\2 和?1, $2轉(zhuǎn)義+數(shù)字
非捕獲組(?:)引用表達(dá)式(()), 本身不被消費(fèi)(?),引用(:)
前向查找(?=)引用子表達(dá)式(()),本身不被消費(fèi)(?), 正向的查找(=)
前向負(fù)查找(?!)引用子表達(dá)式(()),本身不被消費(fèi)(?), 負(fù)向的查找(!)
后向查找(?<=)引用子表達(dá)式(()),本身不被消費(fèi)(?), 后向的(<,開口往后),正的查找(=)
后向負(fù)查找(?<!)引用子表達(dá)式(()),本身不被消費(fèi)(?), 后向的(<,開口往后),負(fù)的查找(!)

邏輯處理

計(jì)算機(jī)科學(xué)就是一門包含邏輯的科學(xué)。讓我們回憶一下編程語言當(dāng)中用到的三種邏輯關(guān)系,與或非。

在正則里面,默認(rèn)的正則規(guī)則都是與的關(guān)系所以這里不討論。

而非關(guān)系,分為兩種情況:一種是字符匹配,另一種是子表達(dá)式匹配。在字符匹配的時(shí)候,需要使用^這個(gè)元字符。在這里要著重記憶一下:只有在[和]內(nèi)部使用的^才表示非的關(guān)系。子表達(dá)式匹配的非關(guān)系就要用到前面介紹的前向負(fù)查找子表達(dá)式(?!regex)或后向負(fù)查找子表達(dá)式(?<!regex)。

或關(guān)系,通常給子表達(dá)式進(jìn)行歸類使用。比如,我同時(shí)匹配a,b兩種情況就可以使用(a|b)這樣的子表達(dá)式。

邏輯關(guān)系正則元字符
[^regex]和!
|

總結(jié)

  對(duì)于正則來說,符號(hào)之抽象往往讓很多程序員卻步。針對(duì)不好記憶的特點(diǎn),我通過分類和聯(lián)想的方式努力讓其變得有意義。我們先從一對(duì)一的單字符,再到多對(duì)多的子字符串介紹,然后通過分組、回溯引用和邏輯處理的方式來構(gòu)建高級(jí)的正則表達(dá)式。

在最后,出個(gè)常用的正則面試題吧:請(qǐng)寫出一個(gè)正則來處理數(shù)字千分位,如12345替換為12,345。請(qǐng)嘗試自己推理演繹得出答案,而不是依靠搜索引擎:)。

?

轉(zhuǎn)載自:正則表達(dá)式不要背——https://www.cnblogs.com/scq000/p/10875941.html

轉(zhuǎn)載于:https://www.cnblogs.com/mufengforward/p/10877551.html

與50位技術(shù)專家面對(duì)面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的正则表达式不用背的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。