正則的用法
const regex = /abc/g
const regex2 = new RegExp('abc', 'g')
regex 和 regex2的匹配內(nèi)容是一樣的,都是全局匹配abc字符串,
只不過regex是用正則表達式字面量,而regex2是調(diào)用RegExp構(gòu)造函數(shù)寫法。
元字符
^ 匹配字符串的開始 /^a/ 表示匹配以a開始
$ 匹配字符串的結(jié)尾 /a$/ 表示匹配以a結(jié)尾
\d [0-9] 表示0-9的任意一位數(shù)字 /^\d/ 表示以一個數(shù)字開始
\D [^0-9] 表示除數(shù)字外的任意字符 /^\D/ 表示不能以數(shù)字開始
\w [0-9a-zA-Z_] 表示數(shù)字、大小寫字母和下劃線
\W [^0-9a-zA-Z_] 表示非單詞字符
\s [\t\v\n\r\f]。表示空白符,包括空格、水平制表符、垂直制表符、換行符、回車符、換頁符
\S [^ \t\v\n\r\f] 非空白符
\b 匹配一個單詞邊界
\B 匹配非單詞邊界
. [^\n\r\u2028\u2029]。通配符,表示幾乎任意字符。換行符、回車符、行分隔符和段分隔符除外。
要是想匹配字符串.需要轉(zhuǎn)譯\.
量詞
{m, n} 出現(xiàn)了m-n次
{m} 出現(xiàn)了m次
{m,} 出現(xiàn)了至少mci
{,n} 最多出現(xiàn)了n次
? 出現(xiàn)了0次或1次, 等價于{0,1}
+ 最少出現(xiàn)了1次,等價于{1,}
* 出現(xiàn)0次或多次,等價于{0,}
貪婪與惰性
貪婪就是盡可能多的匹配,而惰性就是盡可能少的匹配
貪婪匹配寫法:量詞后面加個*
惰性匹配寫法:量詞后面加個?
/.*bbb/g.exec('abbbaabbbaaabbb1234') // abbbaabbbaaabbb
/.*?bbb/g.exec('abbbaabbbaaabbb1234') // abbb
貪婪模式用于匹配優(yōu)先量詞修飾的子表達式
惰性模式用于匹配忽略優(yōu)先量詞修飾子表達式
分組
正則中一對()即為一個分組
/(abx).*/.test('abxsss') 其中(abx) 就是一個分組
可使用構(gòu)造函數(shù)的全局屬性9來獲取
/(abx).*/.exec('abxsss')
console.log(RegExp.$1) // abx
console.log(RegExp.$_) // abxsss
只能表示到9, 如果分組超過了九組,那就沒法表示了
反向引用
/\d{4}(-|\/|\.)\d{2}\1\d{2}/
注意里面的\1,表示的引用之前的那個分組(-|\/|\.)。
不管它匹配到什么(比如-),\1都匹配那個同樣的具體某個字符。
我們知道了\1的含義后,那么\2和\3的概念也就理解了,即分別指代第二個和第三個分組。
這里的分組只是在此之前的分組,在該反向引用之后的無法表示。
/\d{4}(-|\/|\.)\d{2}\2(\d{2})/
這里不能用\2來引用(\d{2})分組,這里只是當作匹配\2去匹配,而不是當作分組的引用
具名分組
/(\d{4}(-|\/|\.))\d{2}\2\d{2}/
這里的第二個分組應(yīng)該是(-|\/|\.)
那/^((\d)(\d(\d)))$/的分組又是怎么分的呢?
對于過多括號嵌套情況,分清分組比較容易出錯
括號嵌套的分組,應(yīng)該從第一個(往里面數(shù),每遇到一個(就是一個新的分組
具名分組就很好的解決了這個問題
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
這里就是用到了具名分組
可以通過\k<month>引用具體是哪個分組
/(?<year>\d{4})-(?<month>\d{2})-\k<month>/
具名分組結(jié)果會放在group屬性里面
非捕獲分組
也可以通過非捕獲分組來降低分組的復(fù)雜度。非捕獲分組是指不會將(?:)中的正則化為一個分組。
/^(?:(?:\d)(\d(\d)))\2$/
這里\2引用的分組是(\d)
零寬斷言(環(huán)視)和負零寬斷言
/z(?=x)y/ 表示z后面緊跟著的是x,但是不匹配x
/z(?!x)y/ 表示z后面緊跟著的不是x,但是不匹配x
/z(?<=x)y/ 表示y前面緊跟著的是x,但是不匹配x
/z(?<!x)y/ 表示y前面緊跟著的不是x,但是不匹配x
修飾符
1. g → global 全局搜索
2. i → ignoreCase 忽略大小寫,大小寫不敏感
3. m → multiline 換行
4. y → sticky “粘連”修飾符 后一次匹配都從上一次匹配成功的下一個位置開始
5. u → unicode Unicode 字符表示法
lastIndex
是正則表達式的一個可讀可寫的整型屬性,用來指定下一次匹配的起始索引。
只有正則表達式使用了表示全局檢索的 "g" 標志時,該屬性才會起作用。此時應(yīng)用下面的規(guī)則:
如果 lastIndex 大于字符串的長度,則 regexp.test 和 regexp.exec 將會匹配失敗,然后 lastIndex 被設(shè)置為 0。
如果 lastIndex 等于字符串的長度,且該正則表達式匹配空字符串,則該正則表達式匹配從 lastIndex 開始的字符串。(then the regular expression matches input starting at lastIndex.)
如果 lastIndex 等于字符串的長度,且該正則表達式不匹配空字符串 ,則該正則表達式不匹配字符串,lastIndex 被設(shè)置為 0.。
否則,lastIndex 被設(shè)置為緊隨最近一次成功匹配的下一個位置。
練習(xí)
1.檢測是否滿足
2016-06-12
2016/06/12
2016.06.12
這三種格式
/\d{4}(-|\/|\.)\d{2}(-|\/|\.)\d{2}/.test('2012/12/13')
這樣寫有一個問題,那就是
/\d{4}(-|\/|\.)\d{2}(-|\/|\.)\d{2}/.test('2012/12-13') 也是true
所以這里就得用到分組引用了
/\d{4}(-|\/|\.)\d{2}\1\d{2}/.test('2012/12-13') false
/\d{4}(-|\/|\.)\d{2}\1\d{2}/.test('2012/12/13') true
這里的\1指的是第一個分組匹配到了什么,這里就是什么
2.將 2016/12/04 改為 12/04/2016
傳統(tǒng)做法可能是需要先以'/'分割字符串,然后在就行字符串拼接
現(xiàn)在可以這樣寫
'2016/12/04'.replace(/(\d{4})\/(\d{2})\/(\d{2})/, '$2/$3/$1')
或者
'2016/12/04'.replace(/(\d{4})\/(\d{2})\/(\d{2})/, function(...arg) {
return arg[2]+'/' + arg[3]+'/' + arg[1]
})
let result = /(?<year>\d{4})\/(?<month>\d{2})\/(?<day>\d{2})/.exec('2016/12/04')
console.log(result)
// 0: "2016/12/04"
// 1: "2016"
// 2: "12"
// 3: "04"
// groups:
// day: "04"
// month: "12"
// year: "2016"
// index: 0
// input: "2016/12/04"
3.可以正確篩選以下規(guī)則
'x=5' 5
'abcx=5' 5
'abc x=5' 5
'x=abc' null
str.match(/(?<=x=)\d+/)
4.可以正確篩選以下規(guī)則
'x=5' 5
'abcx=5' null
'abc x=5' 5
'x=abc' null
這個和上面的區(qū)別就在于x=之前是不是一個邊界
str.match(/(?<=\bx=)\d+/)
不可以用一下的正則匹配
str.match(/(?<=\s+x=)\d+/)
因為這樣的話'x=5'就不匹配了
5.可以正確篩選以下規(guī)則
'one "two three" four five six "seven eight" nine'
to
'"two three" four five six "seven eight"'
str.match(/".*"/g)
6.可以正確篩選以下規(guī)則
'one "two three" four five six "seven eight" nine'
to
['"two three"', '"seven eight"']
str.match(/".*?"/g)
這里和上面的區(qū)別就在于一個是貪婪匹配而這個是惰性匹配
7.可以正確篩選以下規(guī)則
'@@whatever@@@@whatever2@@'
to
'<blink>whatever</blink><blink>whatever2</blink>'
str.replace(/(@@)(.*?)(@@)/g, function(...arg) {
return '<blink>'+ arg[2]+'</blink>'
})
這里不能簡單的將'@@'替換成'<blink>',因為'@@'必須是成對出現(xiàn)的
8.可以正確篩選以下規(guī)則
var text = "First line\nsecond line";
var regex = /(\S+) line\n?/y;
var match = regex.exec(text);
match[1]; // "First"
var match2 = regex.exec(text);
match2[1]// "Second"
var match3 = regex.exec(text);
match3 === null //"true"
這里是開啟粘滯匹配的一個事例
9.測試以下代碼
var re=/^\w$/g
re.test('a') true
re.test('a') false
re.test('a') true
re.test('a') false
re.test('a') true
re.test('a') false
re.test('a') true
re.test('a') false
...
為什么會出現(xiàn)這種情況呢?
這里就涉及到了lastIndex了,
var re=/^\w$/g
re.lastIndex = 0
re.test('a') true
這里正則匹配完成后re.lastIndex = 1
re.test('a') false
因為re.lastIndex = 1了,所以這里的re.test就會失效,當然re.exec也會失效。
可以理解為這時正則是從1這個位置去匹配的,所以匹配不到任何東西.只會就會再次把lastIndex改為0
re.lastIndex = 0
re.test('a') true
又重新從頭開始匹配了,所以這里又再次為true了
結(jié)尾
本文并沒有舉例傳統(tǒng)的一下正則,如郵箱格式,手機號碼格式等,因為這些正則可以搜到很多。本文主要是用到了一下稍微高階一點的正則方法。古人云:用好了正則,可以幫我們省下5000行代碼。