主頁 > 前端設計 > JS 正則運算式詳解 學習筆記

JS 正則運算式詳解 學習筆記

2020-09-16 14:25:26 前端設計

JS 正則運算式:(RegularExpression)

正則運算式的含義:

正則運算式是一種字串匹配模式,在javascript中的作用主要是用來匹配特定形式的字符,

定義一個正則運算式:

我們想要定義一個正則運算式有兩種方法:

  1. 正則運算式直接量
  2. 創建正則運算式物件

正則運算式直接量:

var reg = /s$/

我們可以使用正則運算式直接量來創建一個正則運算式,其中格式為/xxx/,用兩個反斜杠來表示正則運算式創建的格式,反斜杠中間寫上我們要匹配的字符文本,

創建正則運算式物件:

var reg2 = new RegExp("s$")

RegExp建構式創建正則運算式其實也是很簡單的,建構式接收一個引數,引數型別為字串型別,傳入的字串就是我們要匹配的字串的模板,new RegExp()回傳一個正則運算式物件,

注意:和js中其他直接量不同,正則運算式的直接量會在代碼執行的時候會把/s$/轉變成一個RegExp物件,在舊版本的es3中,當執行到同一段正則運算式的時候會回傳相同的物件,舉個例子:

function createRegExp(){
	var reg = /S$/
    return reg
}
var myreg1 = createRegExp()
var myreg2 = createRegExp()
console.log(myreg1 === myreg2)//true

我們定義了一個函式,用來創建一個正則運算式物件,在全域,我們執行了兩次createRegExp()建構式,將生成的正則運算式物件分別存放在myreg1myreg2中,當我們將其做相等運算時發現,它們指向的是相同的一個物件,也就是說,在es3中,用正則運算式常見的RegExp物件會共享同一個實體,其實這個結論的前提是正則運算式的模式要一致,但是在es5中則是兩個相對獨立的運算式物件:

console.log(myreg1 === myreg2)//false

myreg1和myreg2是相互獨立的,并不都會相互共享記憶體資訊,如:lastIndex后續有講到,

直接量字符:

當我們想要去匹配非法的字符的時候,比如/的時候,我們需要對字符進行轉移,通常的做法是在非法的字符前面加上\,即斜杠,

在正則運算式中,有一些字符有它自己獨特的含義,這些字符有:^ $ . * + = ! : | \ / ( ) [ ] { }等,當我們使用這些符號的時候,它代表的意義已經不是我們字面上所看到的了,假如我們真的只是想單純的想去匹配這些字符怎么辦呢,我們需要對這些字符進行轉義,假如我們想要匹配一個+

var reg = /+/

上面的寫法肯定是不行的,原因是它代表特殊的匹配含義,想要單純的匹配字符我們應該這樣:

var reg = /\+/

我們需要用右斜杠將其轉義后就可以單純的匹配+字符了,

字符類:

將直接量字符單獨放進方括號[]內可以組成一個字符類,一個字符類可以匹配它所包含的任意字符,我的理解是,字符類相當于一個集合,這個集合里面放著我們定義的字符,例如:

var reg = /[abc]/

注意:不用打逗號來表示分割,即不要[a,b,c]這樣的話,也成為了我們字符類里的成員,

上面的運算式的意思是:現在我有一個集合,集合里面放的有ab,c這三個元素,那么正則運算式就用這個集合里的任意一個元素進行匹配,另外,我們可以用^來否定字符類,^符號在正則運算式的意思類似于我們編程語言中!的意思,即為非,[^abc]表示剔除掉集合中的所有元素,就是匹配集合的補集,除了集合中的元素,其他的元素都可以匹配,

[]表示的是字符類,但是有一點值得注意,一個[]代表一個字符,只不過這個字符可能是集合中的任意一個字符,什么意思呢,比如:

var str = '1234a'
var reg = /[asdfg]/
//現在我們的集合里面有a,s,d,f,g這五個元素,但是在匹配的時候只能和字串中的一個進行匹配,
//先和str中的1匹配,看自己的集合中有沒有這個字符,如果沒有再和2匹配,看自己的集合里有沒有這個字符
//以此類推,中括號里有五個字符,并不代表它一次能和字串中的五個字符匹配,而是一個一個的匹配,

字符類可以使用連字符-,來表示字符的范圍,如:[a-zA-Z0-9]:表示這個集合里面的元素是大小寫字母和數字,每一個[]其實都有一個自己的集合,就算你這樣寫[(ab)(cd)]他也只是表示他的集合中有(/a/b/)/c/d這六個字符,

在js中,有一些特定的字符類:

  • [...]:方括號內的任意字符,也就是說這個集合里的元素是所有的字符,
  • [^...]:不在方括號內的任意字符,也就是說是[...]集合的補集,也就相當于這個集合為空,它匹配不了任何字符,
  • .:除了換行符和其他Unicode行終止符之外的任意字符,
  • \w:任何ASCII組成的單詞,等價于:[a-zA-Z0-9]
  • \W:(大寫),任何不是ASCII組成的單詞,等價于:[^a-zA-Z0-9]
  • \s:任何Unicode空白符,
  • \S:任何非Unicode空白符的字符,注意:\w\S不一樣,
  • \d:任何ASCII數字,等價于[0-9]
  • \D:除了ASCII數字之外的任何字符,等價于:[^0-9]
  • [\b]:退出直接量(特例)

在方括號內可以寫這些特殊轉義字符,比如:[\w\s]他代表的含義是匹配任何ASCII組成的單詞和任何Unicode空白符,也就是說[\w\s]字符類的集合是\w\s的并集,

重復:

我們現在學的正則運算式只能匹配單個字符,意思是單個單個的字符匹配,無論是字符類還是使用特定的字符類,它們的區別只是對應集合的差異,在匹配的行為上,都是一次只能對一個字符進行匹配,假如我們要對多個字符進行匹配怎么辦,有人會想到,我可以直接/abcd/,這樣就可以一次性匹配四個字符了,也就是四個四個的去匹配,這樣固然可以,但是有局限性,就是限定死了匹配的內容,只能匹配abcd這四個字,現在我們的需求是匹配兩個任意的數字,那么我們應該怎么辦呢?我們可以這樣寫:

var reg = /\d\d/
//這樣就可以表示一次性匹配兩個數字了

但是這樣寫有一個缺點,假如我們要同時匹配100個數字,難道我們要寫100個\d嗎,正則運算式對于重復有自己的相關優化,

下面是正則運算式的重復字符語法:

  • {n,m}:匹配前一項至少n次,但不能超過m次,
  • {n,}:匹配前一項n次或者更多次,
  • {n}:匹配前一項n次,
  • ?:匹配前一項0次或者1次,也就是說前一項是可選的,等價于{0,1}
  • +:匹配前一項1次或多次,等價于{1,}
  • *:匹配前一項0次或多次,等價于{0,}

上面的不是匹配的次數,而是一次要同時匹配幾個字符,即以幾個字符為單位去匹配,

我們來解決我們前面提到的需求,此時我們要匹配兩個數字(兩個兩個的匹配,兩個數字為一次匹配單位),則可以這樣寫:

var reg =/\d{2}/

實體:

這里有幾個實體:

var reg1 = /\d{2,4}/ //匹配2~4個數字
var reg2 = /\w{3}\d?/ //精確匹配三個單詞和一個可選數字
var reg3 = /\s+javascript\s+/ //匹配前后都帶有一個或者多個空格的"javascript"字串
var reg4 = /[^(]*/ //匹配0個或者多個的非(的字符

因為*?允許字符的匹配可以為0,所以它們允許什么都不匹配,

重復的特性:

貪婪匹配:

我們在分析前面正則運算式重復語法的時候發現,大多數的語法都允許我們去匹配多個,現在我們想一個問題,我有一個字串,然后有一個reg

var str = "aaaa"
var reg = /a+/

此時問題就來了,因為+是允許我們去匹配一個或者多個字符,那么我們是匹配一個還是兩個亦或者全部呢?答案是會匹配全部,在一般情況下,正則運算式使用的是貪婪匹配,即盡可能多的匹配,

非貪婪匹配:

假如我們不想進行貪婪匹配,我們可以使用非貪婪匹配,當我們使用非貪婪匹配的時候,只需要在待匹配的字符后面跟隨一個?即可,例如:??,+?,*?,{1,3}?,比如正則運算式/\d{2,4}?/他也可以匹配兩個或四個數字,但是它會盡可能少的匹配,這里挺有意思的,我們仔細思考什么叫盡可能少的匹配,也就是說在默寫特定的情況下即使你使用了?也不能以最少的來匹配,這就引除了一條重要的概念:

正則運算式的模式匹配總是會尋找字串中第一個可能匹配的位置,

例如:

var str = "aaaab"
var reg = /a+?b/

我們也許希望它會匹配最后一個a和第一個b,但是真正的結果是它會匹配整個字串,原因是因為正則運算式的模式匹配總是會尋找字串中第一個可能匹配的位置,

選擇,分組和參考:

正則運算式的語法還包括指定選擇項子運算式分組參考前一子運算式的特殊字符

指定選擇項:在正則運算式中,我們可以使用|來分隔供選擇的字符,例如:/ab|cd|ef/,意思時匹配abcdef,用集合理論的話就是現在的集合中有三個元素ab/cd/ef,看誰最先符合匹配的標準,一旦有一個元素符合匹配的機制就會停止匹配,無論它右面的元素是什么都會被忽略,

選擇項的嘗試匹配次序是從左到右的,直到發現了匹配項,如果左邊的選擇項被匹配的話,則會直接忽略右邊的選擇項,例如:

var str = "ab"
var reg = /a|ab/

因為a被首先的匹配到,所以ab會被自動忽略,

分組()和參考\n

在正則運算式中,圓括號有多種作用:

  1. 把單獨的項組合成子運算式
  2. 在完整的模式中定義子模式
  3. 允許在同一正則運算式的后部分參考前面的子運算式

在正則運算式中,每一個()內的文本都是一個子運算式,有的子運算式可以參考有的卻不能參考,后面有講到,注意:在正則運算式中假如想要匹配’(’ 和 ")"需要進行轉義,

把單獨的項組合成子運算式

? 把單獨的項組合成子運算式,以便可以像處理獨立單元那樣來使用|,?,+等,

? 項其實就是我們每一次進行模式匹配時所用到的字符模板,項的字符數可以是一個或者多個,比如說:

var str = 'ababab'
var reg = /ab|cd|bab/

這里的ab,cd,bab其實就是項,每一次去進行匹配的時候它會使用這三個里面的某一個去匹配,

var str = "abababab"
var str = /ababa/
//此時ababa就是一個項,他是一個整體,不會把它拆開來分別匹配,
var str = "abababab"
var reg = /abab{2}/
console.log(str.match(reg))
null
//我們可以看到,當使用{2}的時候,它前面的項是b,它等價于reg = /ababb/
//所以不同場景,項的字符個數不一樣,

假如我們想要匹配上面字串的abababab怎么辦,當然,你可以直接寫var reg = /abababab/但是這樣寫太麻煩了,我們可以這樣寫/(abab){2}/,此時,{2}會把(abab)看作一個整體,而不是單獨的把最后一個b看作一個整體,其實還有更簡單的方法/(ab){4}/它等價于/abababab/,實體:

var reg = /(ab|cd)+|ef/

上面的正則運算式表示,可以匹配ef,也可以匹配abcd的一次或多次,簡單說,()在這里的作用就是為?/+/*/$/^/{}這些特殊語法服務的,就是想讓它們知道,你要把哪些字符看做一個整體(項),然后一起操作,默認的情況下它們操作的項只有一個字符,

在完整的模式中定義子模式:

允許在同一正則運算式的后部分參考前面的子運算式

帶圓括號運算式的另一個用途是允許在同一正則運算式的后面參考前面的子運算式,這是通過在字符\后面添加數字來實作的,形如:\3原本的數并不需要轉義,但是我們卻用了\字符,說明這個3有特殊含義,數字代表的是帶有圓括號的子運算式在正則運算式的位置,例如:\1代表對正則運算式中的第一個子運算式進行參考,\2表示對正則運算式中的第二個子運算式進行參考,注意,因為子運算式可以嵌套另一個子運算式,所以它的位置是根據參與計數的左括號的位置來決定的,

注意:對正則運算式中前一個子運算式的參考,并不是指對子運算式模式的參考,而指的是與那個模式相匹配的文本的參考,**如何去理解這句話呢,我們看一段代碼:

var reg = /(ab\d)ccc\1/
var str = "ab1cccab1"
console.log(str.match(reg))

我們也許很清楚的知道列印結果:

[ 'ab1cccab1', 'ab1', index: 0, input: 'ab1cccab1', groups: undefined ]

但是匹配的原理真的是你想像中的那樣嗎?我們再來看一個實體:

var reg = /(ab\d)ccc\1/
var str = "ab1cccab2"
console.log(str.match(reg))

結果:

null

為什么是這個樣子,其實上面的那句話說的已經很明了了,對正則運算式前面子運算式的參考并不是對子運算式的模式的參考,什么是模式的參考?模式的參考指的是,比如現在有一個模式是/ab\d/表示匹配ab然后后面跟一個任意的數字,假如有一個正則運算式參考它的模式,則這個正則運算式也表示匹配ab然后后面跟一個任意的數字,但是人家說的很清楚了,不是參考模式而是參考與那個模式相匹配的文本,即\1代表的是(ab\d)匹配的文本,即:ab1,所以當我們設定str = "ab1cccab2"時,就無法得到匹配的原因,

這樣,參考可以用于實施一條約束,即一個字串的各個獨立的部分都是相同的字符,比如:111111111111,假如我們要匹配這樣的一個字串的話,我們完全可以這樣寫:

var str = "aaaaaaaaaaaaa"
var reg = /(\w)(\1)+/
console.log(str.match(reg))

其中/(\w)(\1)+/表示,第一個是任意的字符,那么(\1)表示的是(\w)匹配到的字符,也就是a+表示a的數量可以有多個,那么就可以把整個字串都匹配下來了,

還有一種子運算式:(?:…),例如:(?:java),雖然這是一個運算式,但是它卻不能參考,也就是說它并不在數字索引的考慮范圍之內,例如:

var reg = /(java)(?:script)\2/
var str = "javascriptscript"
console.log(str.match(reg)) 

結果:

null

我們發現,并不能通過\2來獲取script所以(?:)并不能參與正則運算式的數字索引,而它僅僅適用于分組

正則運算式的選擇,分組和參考字符:

  • |:選擇,匹配的是該符號左邊的子運算式或者右邊的子運算式
  • (...):組合,將幾個項組合為一個單元,這個單元可通過*/+/?/|等符號加以修飾,而且可以記住這個組合項匹配的的字串文本以供此后的參考使用,
  • (?:...):只組合,把項組合得到一個單元,這個單元可通過*/+/?/|等符號加以修飾,但不記憶與該組相匹配的字符,其實(?:...)(...)的區別就是一個匹配的文本能被參考而另一個卻不能,
  • \n:n是數字型別,如:\1,和第n個分組第一次匹配的字符相匹配,組是圓括號中的子運算式(也有可能是嵌套的),組的索引是從左到右的左括號數,(?:形式的分組不編碼,

指定匹配位置:

錨點:

在前面我們知道了關于匹配字符的正則運算式,但是,在正則運算式中,還有一種可以用來匹配位置的語法,

在正則運算式中這些匹配位置的語法元素通常我們叫它們錨元素,最常用的錨元素是^,它用來匹配字串的開始,錨元素$用來匹配字串的結束,當然還有\b表示匹配一個單詞的邊界,

零寬斷言:

零寬斷言是一種位置的匹配機制,它匹配到的內容不會保存到匹配結果中去,最終匹配結果只是一個位置而已,
作用是給指定位置添加一個限定條件,用來規定此位置之前或者之后的字符必須滿足限定條件才能使正則中的字運算式匹配成功,

在js中只支持零寬先行斷言,而零寬先行斷言又可以分為正向零寬先行斷言,和負向零寬先行斷言

正向零寬先行斷言(?=…):

var str = "ab123"
var reg = /ab(?=\d+)/
console.log(str.match(reg))
//[ 'ab', index: 0, input: 'ab123', groups: undefined ]

在這里,(?=\d+)表示的是正向零寬斷言,意思是匹配的字符后面(是一個位置概念,類似于^和$)必須有一個或者多個數字才能匹配成功,我們發現,最后它回傳的是ab,并沒有把后面的數字給回傳,因為零寬斷言(?=\d+)并不匹配任何字符,只是用來規定當前位置的后面必須是一個或多個數字,也就是位置的限制條件,要把ab匹配成功,那么它的后面一定要有一個或多個的數字,我們發現,其實和我們熟知的錨點不同,錨點的位置匹配是固定的,沒有任何的限制條件,但是零寬斷言就不一樣了,它必須有限制,

負向零寬先行斷言(?!..):

var str = "ab123abc"
var reg = /ab(?!\d+)/
console.log(str.match(reg))
//[ 'ab', index: 5, input: 'ab123abc', groups: undefined ]

在這里,(?!...)表示的是負向先行斷言,意思時匹配的字符后面必須沒有一個或多個數字才能匹配成功,和正向零寬先行斷言一樣,負向的也只回傳ab,因為零寬斷言(?!\d+)并不匹配任何字符,只是用來規定當前位置的后面必須不能是一個或多個數字,也就是位置的限制條件,這里回傳的index值為5,說明它匹配的ab并不是開頭的那個,而是后面的,我們發現其實正向零寬斷言是表示?=\d+,即這個位置必須有(等于)這個限制條件,而負向零寬斷言是表示?!\d+,即這個位置必須不能有(不等于)這個條件,其實也就是字面意思!(非)不等于或者和正向的條件相反,

通過這兩個實體,其實我們對零寬斷言有一些的了解,我認為零寬斷言彌補了正則中在字串中間定位位置的機制,我們所熟知的錨點是在邊界上定位位置,

修飾符:

正則運算式的修飾符用于更高級的模式匹配,修飾符的寫法和我們平時的匹配模板不同,他并不是用在雙斜杠內部的,而是外部,在第二個反斜杠的右側,如:/\d/gg就是我們所說的修飾符,

正則運算式修飾符:

  • i:執行不區分大小寫,
  • g:執行一個全域匹配,簡而言之,即 找到所有的匹配,而不是在找到第一個后就停止
  • m:多行匹配,^匹配一行的開頭和字串的開頭,$匹配行的結束和字串的結束,

修飾符可以多個同時存在,比如:/javascript\d+/ig表示匹配是全域匹配且不區分大小寫,

用于模式匹配的String方法:

對于正則匹配,我們一般有兩個側重點,一個是對字串的側重,另一個是對正則運算式的側重,

其中在字串中,我們有四種方法使用正則運算式的方法:

  1. search(reg):search方法有一個引數,代表接收的正則運算式,該方法回傳的是第一個符合匹配的起始位置,如果匹配失敗則回傳-1search不支持全域檢索也即是說它會忽略修飾符g,實體:

    console.log("1234".search(/\d/g))
    //0
    

    假如傳入的引數不是正則運算式,則search會將其轉換成正則運算式,例如:str.search("123")會轉換成str.search(/123/)

  2. replace(reg,str):該方法的作用是執行檢索和替換,它接收兩個引數,第一個是要進行匹配的正則運算式,第二個引數是參與替換的字串,該方法會對要匹配的字串進行檢索,將匹配成功的字串替換成第二個引數中的字串,回傳一個替換好的新串,假如使用了模式匹配修飾符g,則不會只匹配一個,而是將整個字串都檢索,符合匹配的都被替換,如果傳入的第一個引數是一個字串,則會直接搜索這個字串,然后進行替換,且只替換一次,因為它并沒有收到全域匹配的指令**,如果在替換字串中出現了$符號加數字,那么replace將用與指定的子運算式相匹配的文本來替換這兩個字符,**replace方法的第二個引數可以是函式,

    該函式能夠動態的機選替換字串,

    插入特殊變數名:

    $$插入一個 “$”,
    $&插入匹配的子串,
    $插入當前匹配的子串左邊的內容,
    $插入當前匹配的子串右邊的內容,
    $n假如第一個引數是 RegExp物件,并且 n 是個小于100的非負整數,那么插入第 n 個括號匹配的字串,提示:索引是從1開始

    實體:

    var str = "asdfg12dfsdf34"
    var reg = /(\d)(\d)/g
    console.log(str.replace(reg,"$2"))
    //asdfg2dfsdf4
    //運行程序大致為,先匹配到了"12",然后用第二個子運算式的文本,也就是"2"來替換匹配到的"12"字串,
    //然后繼續向下匹配,匹配到"34",然后用第二個子運算式的文本,也就是"4"來替換匹配到的"34"字串,
    //然后回傳新串
    

    這里我們可以看到我們用$2匹配到的文本替換了reg匹配到的值,其實本質上,我們就是用第二個引數的值來替換匹配到的值,那么我們就可以靈活發揮了,例如:

    var str = "asdfg12dfsdf34"
    var reg = /(\d)(\d)/g
    console.log(str.replace(reg,"$2" + "AAAA"))
    //asdfg2AAAAdfsdf4AAAA
    //運行程序是這樣的,我們先匹配到了"12",然后$2代表的是第二個子運算式匹配的文本,即"2",然后進行一個
    //字串加法運算,然后把運算的結果當作參與替換的字串,其實本質上,$2就是一個動態的實時再在的值,
    //之所以是動態的,原因在于每次匹配,第二個子運算式的 值會有所變化,
    

    replac方法中的第二個引數可以為一個函式,我們可以通過函式來對匹配的字符進行操作:

    var str = "asdfg12dfsdf34"
    var reg = /\d\d/g
    console.log(str.replace(reg,function(val){
        console.log(val)
        return "AA" + val
    }))
    

    結果:

    12
    34
    asdfgAA12dfsdfAA34
    
  3. match(reg):該方法是最常用的方法之一,該方法只有一個引數,該引數是我們要匹配的正則運算式,該方法的回傳值是一個陣列,若我們使用了修飾符g,則該方法會進行全域匹配,而回傳的陣列中就是全域匹配成功的字符,假如我們并不進行全域匹配,那么仍然回傳一個陣列,該陣列的一個元素是我們匹配到的字符,而剩下的元素是子運算式匹配的文本,如:

    var str = "123"
    var reg2 = /\d/
    var reg1 = /\d/g
    var reg = /(\d)(\d)(\d)/
    console.log(str.match(reg))
    console.log(str.match(reg1))
    console.log(str.match(reg2))
    

    結果:

    [ '123', '1', '2', '3', index: 0, input: '123', groups: undefined ]
    [ '1', '2', '3' ]
    [ '1', index: 0, input: '123', groups: undefined ]
    
  4. split(string/reg):該方法用以將呼叫它的字串拆分為一個子串組成的陣列,使用的分割符就是傳入的引數,例如

    console.log("1,2,3,4,5".split(","))
    //[ '1', '2', '3', '4', '5' ]
    console.log("1-2-3-4-5".split("-"))
    //['1','2','3','4','5']
    console.log("1 - 2".split(/\s-\s/))
    //['1','2']
    console.log("1 - 2 -        3".split(/\s*-\s*/))
    //[ '1', '2', '3' ]
    

    當我們傳入正則運算式時,split會在字串中匹配相應的字符,將匹配成功的字符作為分隔符來進行分割字串,

RegExp物件:

我們在前面說過,當js引擎在對正則運算式的處理是:假如我們用直接量的方式去創建一個正則運算式,那么js引擎會把它轉化成正則物件,想要獲取正則運算式物件,可以通過RegExp建構式:

var reg = new RegExp("abc")

這樣我們就獲得了一個匹配字串abc的一個正則運算式物件,RegExp可以接收兩個字串引數,第一個引數是我們要匹配的字串模板,也就是直接量正則運算式兩個斜杠之間的文本,在這里需要注意,假如我們使用像\d\s\w\W等這種帶有\字符的這種字符類,必須要把\進行轉義,比如:

var reg = new RegExp("\\d")

在這里我們使用了\\d中的\進行了轉義,RegExp建構式的第二個引數是一個可選引數,它指定的是正則運算式的修飾符,不過只能傳入i/g/m或者它們的組合,例如:

var reg = new RegExp("\\d","g")

既然是物件,那么就會有屬性和方法,接下來我們來看一看正則運算式物件的屬性和方法,

屬性:

  1. source:只讀,字串型別,包含正則運算式的文本,
  2. global:只讀,布爾型別,用以說明運算式是否含有修飾符g
  3. ignoreCase:只讀,布爾型別,用以說明運算式是否含有修飾符i
  4. multiline:只讀,布爾型別,用以說明運算式是否含有修飾符m
  5. lastIndex:可讀寫,number型別,如果匹配模式中使用了修飾符g,這個屬性存盤的是在整個字串下一次檢索開始的位置,這個屬性在后面的物件方法中會用到,

方法:

  1. exec():該函式的第一個引數是待檢索的字串,可以傳入第二引數,第二個引數是修飾符,實體:

    var str = "abcde"
    var reg = new RegExp("\\w\\w")
    console.log(reg.exec(str))
    //[ 'ab', index: 0, input: 'abcde', groups: undefined ]
    

    我們從代碼中可以看到,exec方法對一個指定的字串執行一個正則運算式,簡言之,就是在一個字符中執行檢索匹配,如果沒有找到任何匹配那就回傳null,但是如果找到了一個匹配,它將回傳一個陣列,然后不會再向后進行匹配,這個陣列的第一個元素包含的是與正則運算式相匹配的字串,余下的元素是與圓括號內的子運算式相匹配的子串,例如:

    var str = "abcde"
    var reg = new RegExp("(\\w\\w)(\\w\\w)")
    console.log(reg.exec(str))
    //[ 'abcd', 'ab', 'cd', index: 0, input: 'abcde', groups: undefined ]
    

    match方法不同,就算是你使用了修飾符g,單次執行該函式是不會去進行全域的匹配,實體:

    var str = "123"
    var reg = new RegExp("\\d","g")
    console.log(reg.exec(str))
    //[ '1', index: 0, input: '123', groups: undefined ]
    

    match方法單次執行會進行全域匹配:

    var str = "123"
    var reg = new RegExp("\\d","g")
    console.log(str.match(reg))
    //[ '1', '2', '3' ]
    

    雖然exec的功能簡單,但是多次執行時有一點特別需要注意,那就是它是否帶有修飾符g,我們來看一個例子:

    var str = "abcdefghijklmnopqrst"
    var reg = new RegExp("\\w\\w")
    console.log(reg.exec(str))
    console.log(reg.exec(str))
    console.log(reg.exec(str))
    console.log(reg.exec(str))
    console.log(reg.exec(str))
    console.log(reg.exec(str))
    /*
    [ 'ab', index: 0, input: 'abcdefghijklmnopqrst', groups: undefined ]
    [ 'ab', index: 0, input: 'abcdefghijklmnopqrst', groups: undefined ]
    [ 'ab', index: 0, input: 'abcdefghijklmnopqrst', groups: undefined ]
    [ 'ab', index: 0, input: 'abcdefghijklmnopqrst', groups: undefined ]
    [ 'ab', index: 0, input: 'abcdefghijklmnopqrst', groups: undefined ]
    [ 'ab', index: 0, input: 'abcdefghijklmnopqrst', groups: undefined ]
    */
    

    然后我們再來看一個例子:

    var str = "abcdefghijklmnopqrst"
    var reg = new RegExp("\\w\\w","g")
    console.log(reg.exec(str))
    console.log(reg.exec(str))
    console.log(reg.exec(str))
    console.log(reg.exec(str))
    console.log(reg.exec(str))
    console.log(reg.exec(str))
    /*
    [ 'ab', index: 0, input: 'abcdefghijklmnopqrst', groups: undefined ]
    [ 'cd', index: 2, input: 'abcdefghijklmnopqrst', groups: undefined ]
    [ 'ef', index: 4, input: 'abcdefghijklmnopqrst', groups: undefined ]
    [ 'gh', index: 6, input: 'abcdefghijklmnopqrst', groups: undefined ]
    [ 'ij', index: 8, input: 'abcdefghijklmnopqrst', groups: undefined ]
    [ 'kl', index: 10, input: 'abcdefghijklmnopqrst', groups: undefined ]
    */
    

    我們觀察上面的代碼,我們可以發現,第一個實體是我們不加修飾符g,第二個實體我們加上了修飾符g,我們連續對同一個字串呼叫exec我們可以從列印的結果上看,它們有天壤之別,在第一個實體當中,匹配的元素永遠是ab,且index屬性的值永遠是0,而第二個實體中,匹配的是不同的字符,并且index屬性的值一直是變化的,這就是修飾符gexec函式的宏觀影響,當呼叫exec函式的正則運算式物件具有修飾符g時,它將把當前正則運算式物件的lastIndex屬性設定為緊挨著匹配子串的字符位置,當同一正則運算式第二次呼叫exec函式時,他將從lastIndex所指示的字符處開始檢索,如果exec沒有檢測到任何匹配結果,他會將lastIndex重置為0,這里有一個很有意思的實體:

    var str = "abcdefghijklmnopqrst"
    var str_2 = "zzxxccvvbb"
    var reg = new RegExp("\\w\\w","g")
    console.log(reg.exec(str))
    console.log(reg.exec(str))
    console.log(reg.exec(str_2))
    /*
    [ 'ab', index: 0, input: 'abcdefghijklmnopqrst', groups: undefined ]
    [ 'cd', index: 2, input: 'abcdefghijklmnopqrst', groups: undefined ]
    [ 'cc', index: 4, input: 'zzxxccvvbb', groups: undefined ]
    */
    

    我們可以從實體中對上面的描述會有一個更清楚的理解,當我們第一次執行reg.exec(str)后,此時的reg.lastIndex的值為2,當我們第二次執行reg.exec(str)時,因為需要從reg物件中的lastindex屬性中獲取我檢索的位子,此時的reg.lastIndex的值為2,所以就從2位置開始檢索,檢索完畢后將reg.lastIndex的值設定為4并保存,當去匹配其他字串的時候,需要從lastIndex獲取開始匹配的位置,所以其他的字串會從4這個位置開始匹配,

  2. test(str):該函式接收一個引數,就是我們所要匹配的字串,該函式的作用是檢測目標字串中是否有我們要匹配的字符,如果有就回傳true,如果沒有就回傳false,實體:

    var str = "1asdf"
    var reg = new RegExp("\\d")
    console.log(reg.test(str))
    //true
    

    當我們不添加修飾符g的時候,和exec方法一樣,reg.lastIndex的值永遠為0,每一次的匹配都要從最開始來匹配,但是當我們加上修飾符g的時候,事情就變得好玩了,我們來看一個實體:

    var str = "asd1f"
    var reg = new RegExp("\\d","g")
    console.log(reg.lastIndex)
    console.log(reg.test(str))
    console.log(reg.lastIndex)
    console.log(reg.test(str))
    console.log(reg.lastIndex)
    /*
    0
    true
    4
    false
    0
    */
    

    我們發現,當我們使用修飾符g的時候,和exec一樣,當匹配成功時,lastIndex會被設定為緊挨著匹配子串的字符位置,當有其他的字符呼叫該正則運算式的test函式時,開始檢索的位置就是reg.lastIndex保存的位置,當匹配失敗,則會重置lastIndex的值為0

    exectest不同的是,字串上的方法并不會用到lastIndex這個屬性,本質上,String方法只是簡單地將lastIndex的值重置為0,所以我們每次呼叫相關方法的時候都會從頭開始檢索,

感想:

上述的文章是我在學習js正則運算式自己歸納和理解的,僅供大家參考,若有錯誤,望指正,我將感激不盡,謝謝!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/57747.html

標籤:其他

上一篇:學成在線頁面制作(HTML+CSS部分)(頂部+輪播圖部分)

下一篇:Web程式設計筆記(四)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more