前端的正則表達式

正則的用法

  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ù)的全局屬性1至9來獲取

/(abx).*/.exec('abxsss')
console.log(RegExp.$1)  // abx
console.log(RegExp.$_)  // abxsss

只能表示到1-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行代碼。

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

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

  • 初衷:看了很多視頻、文章,最后卻通通忘記了,別人的知識依舊是別人的,自己卻什么都沒獲得。此系列文章旨在加深自己的印...
    DCbryant閱讀 4,251評論 0 20
  • 注:參考《JavaScript語言精粹》第七章和第八章。 一、正則表達式的概念,作用 1.概念:正則表達式是一門簡...
    yaya520閱讀 910評論 0 1
  • 1. 概述 正則表達式(regular expression)是一種表達文本模式(即字符串結(jié)構(gòu))的方法,有點像字符...
    JRG_Orange閱讀 2,711評論 0 50
  • 任務(wù)效果預(yù)覽源碼地址 一、創(chuàng)建一個正則表達式 1 字面量方式: 其中的pattern可以是任何簡單的或者復(fù)雜的正則...
    起這么長的名字根本沒有用閱讀 3,012評論 0 13
  • JS正則表達式一條龍講解,從原理和語法到JS正則、ES6正則擴展,最后再到正則實踐思路 Stinson 關(guān)注 20...
    小杰的簡書閱讀 713評論 0 2

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