JS 正則表達式必知必會

在實際作項目過程當中會常常遇到須要驗證的狀況,這時候若是對正則表達式不夠了解,就須要常常去網上找,顯得low,爲了可以本身手寫正則,在下花功夫作了一些筆記,而且用這些知識作了一個正則的代碼庫,方便平時本身使用。html

聲明:前端

  1. ES9表明ES2018,若是特性後加了ES9,那麼表明是ES2018中新增的特性

1. 簡介

正則表達式(Regular Expression) 是對字符串操做的一種邏輯公式,就是用事先定義好的一些特定字符、及這些特定字符的組合,組成一個規則字符串,這個規則字符串用來表達對字符串的一種過濾邏輯。面試

簡單來講就是:按照某種規則去匹配符合條件的字符串。正則表達式的規則是/ pattern / flags正則表達式

可使用字面量形式或者new的方式來建立正則表達式segmentfault

// 使用直接字面量建立 ,推薦使用,性能更好,注意這裏pattern不能爲空,否則覺得是註釋
var exp1 = /\d/g
 
// 使用RegExp對象建立
var exp2 = new RegExp('\d', 'g');

模式中使用的全部元字符都建議在以前加\轉義,正則表達式中的元字符包括:數組

( [ { \ ^ $ | ) ? * + . ] }

2. 內容

2.1 匹配模式

修飾符表示正則表達式的匹配模式微信

修飾符 描述
i 執行對大小寫不敏感的匹配
g 執行全局匹配,查找全部匹配而非在找到第一個匹配後中止
m 執行多行匹配,會改變^$的行爲
u 能夠匹配4字節的unicode編碼
s (ES9) dotAll模式,.能夠匹配換行符

加了u修飾符,會正確處理大於\uFFFF的unicode,好比4字節的🐪 \uD83D\uDC2Aide

/^\uD83D/.test('\uD83D\uDC2A')                //  true
/^\uD83D/u.test('\uD83D\uDC2A')               //  false

默認狀況下,.能夠匹配任意字符,除了換行符,且.不能匹配Unicode字符,須要使用u選項啓用Unicode模式才行。函數

ES2018引入了dotAll模式,經過s選項能夠啓用,這樣,.就能夠匹配換行符了。工具

/foo.bar/.test('foo\nbar');                 // false
/foo.bar/s.test('foo\nbar');                 // true

2.2 類

使用[ ]來表達,用於查找某個範圍內的字符

表達式 描述
[abc] 查找方括號之間的任何字符
[0-9] 查找任何從 0 至 9 的數字

還有一些預約義類方便咱們直接使用:

預約義類 等價 描述
\s [\t\n\x0B\f\r] 空格
\S [^\t\n\x0B\f\r] 非空格
\d [0-9] 數字
\D [^0-9] 非數字
\w [a-zA-Z_0-9] 單詞字符 ( 字母、數字、下劃線)
\W [^a-zA-Z_0-9] 非單詞字符
. [^\r\n] 任意字符,除了回車與換行外全部字符
\f \x0c \cL 匹配一個換頁符
\n \x0a \cJ 匹配一個換行符
\r \x0d \cM 匹配一個回車符
\t \x09 \cI 匹配一個製表符
\v \x0b \cK 匹配一個垂直製表符
\xxx 查找以八進制數 xxx 規定的字符
\xdd 查找以十六進制數 dd 規定的字符
\uxxxx 查找以十六進制數 xxxx 規定的 Unicode 字符

2.3 量詞

量詞表示匹配多少個目標對象,精確匹配長度使用{ }

量詞 等價 描述
n* {0,} 匹配零個或多個n
n+ {1,} 匹配至少一個 n 的字符串
n? {0,1} 匹配零個或一個n
{n} 匹配n次
{n,m} 匹配n到m次
{n,} 至少匹配n次

2.4 邊界

邊界 描述
^ 以xx開始,在類[ ]中表示非
$ 以xx結束
\b 單詞邊界
\B 非單詞邊界

^匹配字符串開始位置,也就是位置0,若是設置了 RegExp 對象的 Multiline 屬性m^ 也匹配 'n' 或 'r' 以後的位置

$通常匹配字符串結束位置,若是設置了 RegExp 對象的 Multiline 屬性m$ 也匹配 'n' 或 'r' 以前的位置

\b匹配一個單詞邊界,也就是指單詞和空格間的位置,如 er\b能夠匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'

\B匹配非單詞邊界。如 er\B能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'

2.5 分組

分組使用( ),做用是提取相匹配的字符串,使量詞做用於分組
好比hehe{3}是把e匹配了3次而不是單詞,若是但願做用於單詞,可使用分組(hehe){3}

分組中使用 | 能夠達到的效果
好比:T(oo|ii)m能夠匹配 Toom 和 Tiim

`abToomhaTiimmm`.replace(/T(oo|ii)m/g, '-')                // ab-ha-mm

反向引用

使用( )後可使用$1-$9等來匹配

'2018-02-11'.replace(/(\d{4})\-(\d{2})\-(\d{2})/g, '$2/$3/$1')            //  02/11/2018

後向引用

\n 表示後向引用,\1是指在正則表達式中,從左往右數第1個( )中的內容;以此類推,\2表示第2個( )\0表示整個表達式。

//匹配日期格式,表達式中的\1表明重複(\-|\/|.)
var rgx = /\d{4}(\-|\/|.)\d{1,2}\1\d{1,2}/
rgx.test("2016-03-26")             // true
rgx.test("2016-03.26")             // false

後向引用和反向引用的區別是:\n只能用在表達式中,而$n只能用在表達式以外的地方。

分組命名 (ES9)

ES2018 以前的分組是經過數字命名的:

const pattern = /(\d{4})-(\d{2})-(\d{2})/u;
const result = pattern.exec('2018-10-25');
console.log(result[0]); // 打印"2018-10-25"
console.log(result[1]); // 打印"2018"
console.log(result[2]); // 打印"10"
console.log(result[3]); // 打印"25"

如今能夠經過指定分組的名稱,增長代碼可讀性,便於維護:

const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
const result = pattern.exec('2018-10-25');
console.log(result.groups.year);         // 打印"2018"
console.log(result.groups.month);        // 打印"10"
console.log(result.groups.day);          // 打印"25"

分組命名還能夠和String.prototype.replace方法結合:

const reDate = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const d = '2018-10-25';
const USADate = d.replace(reDate, '$<month>-$<day>-$<year>');
console.log(USADate);                        // 10-25-2018

忽略分組

若是不但願捕獲某些分組,在分組內加上?:便可
好比(?:tom).(ok)那麼這裏$1指的就是ok

前瞻

前瞻 Lookahead 是RegExp匹配到規則的時候,向後檢查是否符合斷言,後顧JS目前不支持 (ES2018已經支持)

名稱 正則 描述
正向前瞻 (?= ) 後面要有xx
負向前瞻 (?! ) 後面不能有xx
'1a2bc*456v8'.replace(/\w(?=\d)/g, '-')        //  1-2bc*--6-8    匹配後面是數字的單詞字符
'1a2bc*456v8'.replace(/\w(?!\d)/g, '-')               //  -a---*45-v-    匹配後面不是數字的單詞字符
 
const pattern1 = /\d+(?= dollars)/u;             // 正向前瞻,匹配字符串中緊跟着是dollars的數字
const result1 = pattern1.exec('42 dollars');
console.log(result1[0])                                    // 打印42
 
const pattern2 = /\d+(?! dollars)/u;           // 負向前瞻,匹配字符串中緊跟着的不是dollars的數字
const result2 = pattern2.exec('42 pesos');
console.log(result2[0]);                                     // 打印42

(?=exp)匹配一個位置,這個位置的右邊能匹配表達式exp,注意這個表達式僅僅匹配一個位置,只是它對於這個位置的右邊有要求,而右邊的東西是不會被放進結果的,好比用read(?=ing)去匹配"reading",結果是"read",而"ing"是不會放進結果的。

舉個栗子,對密碼應用如下限制:其長度必須介於 4 到 8 個字符之間,而且必須至少包含一個數字,正則是/^(?=.*\d).{4,8}$/

後顧 (ES9)

後顧 Lookbehind 是RegExp匹配到規則的時候,向前檢查是否符合斷言

名稱 正則 描述
正向後顧 (?<= ) 前面要有xx
負向後顧 (?<! ) 前面不能有xx
const pattern1 = /(?<=\$)\d+/u;           // 正向後顧,匹配字符串中前面是\$的數字
const result1 = pattern1.exec('$42');
console.log(result1[0]);                  // 打印42
 
const pattern2 = /(?<!\$)\d+/u;           // 負向後顧,匹配字符串中前面不是是\$的數字
const result2 = pattern2.exec('€42');
console.log(result2[0]);                  // 打印42

2.6 貪婪模式 與 非貪婪模式

正則表達式在匹配的時候默認會盡量多的匹配,叫貪婪模式。經過在限定符後加?能夠進行非貪婪匹配
好比\d{3,6}默認會匹配6個數字而不是3個,在量詞{ }後加一個?就能夠修改爲非貪婪模式,匹配3次

`12345678`.replace(/\d{3,6}/, '-')                // -78
`12345678`.replace(/\d{3,6}?/, '-')                // -45678
'abbbb'.replace(/ab+?/, '-')                //  -bbb

2.7 優先級

優先級從高到低:

  1. 轉義 \
  2. 括號( )(?: )(?= )[ ]
  3. 字符和位置
  4. |

3. 經常使用屬性與方法

3.1 RegExp構造函數屬性

RegExp構造函數上也包含一些屬性,這些屬性適用於做用域中全部的正則表達式,而且基於所執行的最近一次正則表達式操做而變化,這些屬性分別有一個長屬性名和短屬性名

長屬性名 短屬性名 描述
input $_ 返回執行規範表述查找的字符串。只讀
lastMatch $& 返回任何正則表達式搜索過程當中的最後匹配的字符。只讀
lastParen $+ 若是有的話,返回任何正則表達式查找過程當中最後括的子(分組)匹配。只讀
leftContext $\ 返回被查找的字符串中從字符串開始位置到最後匹配以前的位置之間的字符。只讀
rightContext $' 返回被搜索的字符串中從最後一個匹配位置開始到字符串結尾之間的字符。只讀

3.2 RegExp實例上的屬性

屬性 描述
global 是否全文搜索,默認false,對應修飾符的g,只讀
ignoreCase 是否大小寫敏感,默認false,對應修飾符i,只讀
multiline 是否多行搜索,默認false,對應修飾符m,只讀
flags 返回修飾符,只讀
lastIndex 當前表達式匹配內容的最後一個字符的下一個位置
source 正則表達式的文本字符串

3.3 經常使用方法

RegExp.prototype.test(str)

測試字符串參數中是否存在匹配正則表達式的字符串,使用.test的時候若是修飾符有g ,那麼會正則會記住lastIndex並在下一次執行的時候從lastIndex處開始檢測,若是隻是爲了測試是否符合正則,能夠不用g或者每次都從新實例化正則表達式

const reg=/\w/g
reg.test('a')                 //  true
reg.test('a')                 //  false

RegExp.prototype.exec(str)

使用正則表達式對字符串執行搜索,並將更新全局RegExp對象的屬性以反映匹配結果

若是匹配失敗,exec() 方法返回 null

若是匹配成功,exec() 方法返回一個數組,並更新正則表達式對象的屬性

  • 數組索引0:匹配的所有字符串
  • 數組索引1,2..n:括號中的分組捕獲
  • index:屬性是匹配文本的第一個字符的位置
  • input:存放被檢索的字符串

要注意的是:

  1. exec()永遠只返回一個匹配項(指匹配整個正則的)
  2. 若是設置了g修飾符,每次調用exec()會在字符串中繼續查找新匹配項,不設置g修飾符,對一個字符串每次調用exec()永遠只返回第一個匹配項。因此若是要匹配一個字符串中的全部須要匹配的地方,那麼能夠設置g修飾符,而後經過循環不斷調用exec方法。
//匹配全部ing結尾的單詞
const str="Reading and Writing"
const pattern=/\b([a-zA-Z]+)ing\b/g
let matches
while(matches=pattern.exec(str)){
  console.log(matches.index +' '+ matches[0] + ' ' + matches[1]);
}
// 0 Reading Read
// 12 Writing Writ

String.prototype.search(reg)

search() 方法用於檢索字符串中指定的子字符串,或檢索與正則表達式相匹配的子字符串
方法返回第一個匹配結果的index,查找不到返回-1
search() 方法不執行全局匹配,它將忽略修飾符g,而且老是從字符串的開始進行檢索

String.prototype.split(reg)

split() 方法通常用來分割字符串成數組,也能夠傳入正則表達式,使用正則能夠避免一些傳入字符串解決不了的問題

'a1b2c3d4e'.split(/\d/)                   //  ["a", "b", "c", "d", "e"]
'a b   c'.split(' ')                      // ['a', 'b', '', '', 'c']   沒法識別連續空格
'a b   c'.split(/\s*/)                    // ['a', 'b', 'c']

String.prototype.match(reg)

match() 方法將檢索字符串,以找到一個或多個與reg相匹配的文本,reg是否有修飾符g影響很大
返回值與RegExp.prototype.exec的返回相似,不過只返回匹配的字符串數組

'cdbbdbsdbdbzddzdbbbd'.match(/d(b+)d/g)    //   ["dbbd", "dbd", "dbbbd"]
'cdbbdbsdbdbzddzdbbbd'.match(/d(b+)d/)     //   ["dbbd", "bb", index: 1, input: "cdbbdbsdbdbzddzdbbbd"]

若是修飾符有g則匹配出全部匹配的數組,若是不是,則出第一個匹配的字符串,以及相應的捕獲內容

String.prototype. replace (reg, str | num | function)

找到匹配並替換,傳入string、number比較常見,這裏傳入回調function是比較高級的用法,這裏能夠參考MDN

好比一個場景,把手機號的中間4位換成*

function validateMobile(str) {
  return /^[1][0-9]{10}$/.test(str) &&
      str.replace(/(\d{3})(\d{4})(\d{4})/, (rs, $1, $2, $3) => `${$1}****${$3}`)
}

也能夠不返回值,用回調來遍歷,好比一個在面試中會遇到的問題:找出重複最多的字符

let str = 'asss23sjdssskssa7lsssdkjsssdss'
 
const arr = str.split(/\s*/)             // 把字符串轉換爲數組
const str2 = arr.sort().join('')         // 首先進行排序,這樣結果會把相同的字符放在一塊兒,而後再轉換爲字符串
 
let value = ''
let index = 0
str2.replace(/(\w)\1*/g, function($0, $1) {         //匹配字符
  if (index < $0.length) {
    index = $0.length                   // index是出現次數
    value = $1                              // value是對應字符
  }
})
 
console.log(`最多的字符: ${value} ,重複的次數: ${index}`)          // s   17

網上的帖子大多深淺不一,甚至有些先後矛盾,在下的文章都是學習過程當中的總結,若是發現錯誤,歡迎留言指出~

更新記錄:

  • 20180923: 添加ES9的更新內容

參考:

  1. 慕課網 - JavaScript正則表達式
  2. MDN - RegExp
  3. JavaScript 中的正則表達式
  4. 過目不忘JS正則表達式
  5. JS 進階- test, exec, match, replace
  6. ECMAScript正則表達式6個最新特性 - ES2018
  7. ES9已經來了 Are you ready?

推介工具:

  1. 正則表達式軌道圖生成工具 - regexper

工具庫:

  1. JS 活學活用正則表達式

PS:歡迎你們關注個人公衆號【前端下午茶】,一塊兒加油吧~

另外能夠加入「前端下午茶交流羣」微信羣,長按識別下面二維碼便可加我好友,備註加羣,我拉你入羣~

相關文章
相關標籤/搜索