動態

詳情 返回 返回

玩轉前端正則表達式 - 動態 詳情

文章首發本人博客,由於格式和圖片解析問題,可以前往 閲讀原文

JavaScript中的正則是Perl的大子集,但Perl內部的一些表達式卻沒有繼承

正則表達式是用於匹配字符串中字符組合的模式(可參考MDN教程)

掃碼關注公粽號,查看更多優質文章

一個例子

使用正則將一個數字以科學計數法進行表示,如:

 // 10000000 => 10,000,000

現在用一個正則來解決

const str = '10000000'
const reg = /(?=(\B)(\d{3})+$)/g
str.replace(reg, ',') // 10,000,000

修飾符

正則表達式是由模式修飾符(可有可無)組成
常用的修飾符有:igmuy

  • i:匹配時不區分大小寫

    str = 'JavaScript'
    reg = /javascript/i
    reg.test(str) // => true
  • g:匹配時會查找所有的匹配項,而不只是第一個

    str = 'absKsdblsb'
    reg = /s/g
    str.match(reg) // => [s, s, s]
  • m:匹配多行(會影響^、$操作符)

    str = 'ab\nab'
    reg = /^ab/mg
    str.match(reg) // => [ab,ab]
  • u:開啓完整的 unicode 支持
  • y:粘滯模式

    str = 'Hello, world!';
    reg = /\w+/y;
    reg.lastIndex = 5; // 在位置 5 精確查找
    regexp.exec(str) // => null

字符類

字符類是個特殊的字符,用來匹配字符集中字符
如:匹配字符串中的數字

const str = 'jhsd123fg903d'

// 匹配所有的數字
str.match(/\d/g) // => ['1', '2', '3', '9', '0', '3']

常用的字符表達式見下表:

表達式 描述 含義
. 匹配單個字符,除了換行和行結束符 (小數點)默認匹配除換行符之外的任何單個字符。例如,/.n/ 將會匹配 "nay, an apple is on the tree" 中的 'an' 和 'on',但是不會匹配 'nay'。如果 s ("dotAll") 標誌位被設為 true,它也會匹配換行符。
\w 匹配單詞字符 匹配一個單字字符(字母、數字或者下劃線)。等價於 [A-Za-z0-9_]。例如, /\w/ 匹配 "apple," 中的 'a',"$5.28,"中的 '5' 和 "3D." 中的 '3'。
\W 匹配非單詞字符 匹配一個非單字字符。等價於 1。例如, /\W/ 或者 /1/ 匹配 "50%." 中的 '%'。
\d 匹配數字 匹配一個數字。等價於[0-9]。例如, /\d/ 或者 /[0-9]/ 匹配"B2 is the suite number."中的'2'。
\D 匹配非數字字符 匹配一個非數字字符。等價於2。例如, /\D/ 或者 /2/ 匹配"B2 is the suite number."中的'B' 。
\s 匹配空白字符 匹配一個空白字符,包括空格、製表符、換頁符和換行符。等價於[ \f\n\r\t\v\u00a0\u1680\u180e\u2000-u200a\u2028\u2029\u202f\u205f\u3000\ufeff]。例如, /\s\w*/ 匹配"foo bar."中的' bar'。經測試,\s不匹配"\u180e",在當前版本Chrome(v80.0.3987.122)和Firefox(76.0.1)控制枱輸入/\s/.test("\u180e")均返回false。
\S 匹配非空白字符 匹配一個非空白字符。等價於 3。例如,/\S\w*/ 匹配"foo bar."中的'foo'。
\b 匹配單詞邊界 匹配一個詞的邊界。一個詞的邊界就是一個詞不被另外一個“字”字符跟隨的位置或者前面跟其他“字”字符的位置,例如在字母和空格之間。注意,匹配中不包括匹配的字邊界。換句話説,一個匹配的詞的邊界的內容的長度是0。(不要和[\b]混淆了)使用"moon"舉例:/\bm/匹配“moon”中的‘m’;/oo\b/並不匹配"moon"中的'oo',因為'oo'被一個“字”字符'n'緊跟着。/oon\b/匹配"moon"中的'oon',因為'oon'是這個字符串的結束部分。這樣他沒有被一個“字”字符緊跟着。/\w\b\w/將不能匹配任何字符串,因為在一個單詞中間的字符永遠也不可能同時滿足沒有“字”字符跟隨和有“字”字符跟隨兩種情況。
\B 匹配非單詞邊界 匹配一個非單詞邊界。匹配如下幾種情況:
(1)字符串第一個字符為非“字”字符
(2)字符串最後一個字符為非“字”字符
(3)兩個單詞字符之間
(4)兩個非單詞字符之間
(5)空字符串
\n 匹配換行符 匹配一個換行符 (U+000A)。

...等等其他

例子

  1. .字符

    var str = 'aa1bb'
    str.match(/(\w)\1.(\w)\2/g) // => ['aa1bb'],此處 `.` 匹配的是 1
    
    str = 'nay, an apple is on the tree'
    str.match(/.n/g) // => ['an', 'on'],匹配兩個結果,此處`.` 分別是 a, o
  2. \w字符

    var str = 'a1ds23;.?]'
    str.match(/\w/g) // => ['a', '1', 'd', 's', '2', '3']
  3. \d字符

    var str = 'a1ds23;.?]'
    str.match(/\w/g) // => ['1', '2', '3']
  4. \s字符

    var str = 'a1 ds2 3;.?]'
    str.match(/\w/g) // => [' ', ' '],共有兩處
  5. \b字符

    var str = '2split1;word3'
    str.match(/\b\d/g) // => [2],先匹配數字[2,1,3],而後單詞邊界的是 [2]
  6. \B字符

    var str = '2split1;word3'
    str.match(/\B\d/g) // => [1, 3],先匹配數字[2,1,3],而後非單詞邊界的是 [1,3]

複用

複用即量詞,常見的量詞包括:*+?{n},量詞可以在匹配多個相同的模式的時候很管用,避免了重複寫相同的模式

一個小例子:

str = 'ab1c2d345e'
str.match(/\d/ig) // => [1, 2, 3, 4, 5]

// 使用量詞 (這裏涉及到了貪婪原則,後面會講到)
str.match(/\d+/ig) // => [1, 2, 345]

來看下面的解釋

  • *:匹配前面的模式0個或多個,相當於{0, n}

    // 還是上面的例子
    // 使用 *
    str.match(/\d*/ig) // => ['', '', '1', '', '2', '', '345', '', '']
    // 這裏因為 * 0個也會匹配,所以每個位置都會匹配到空字符
  • +:匹配前面的模式1個或多個,相當於{1, n}

    // 使用 +,同理,至少得一個才能匹配上
    str.match(/\d+/ig) // => ['1', '2', '345']
  • ?:匹配前面模式0個或1個,相當於{0, 1}

    str = 'Should I write color or colour'
    // 這裏 ? 表示前面的 u 有或者沒有都行
    str.match(/colou?r/ig) // => [color, colour]
  • {n}:表示一個範圍,可以是{0,n},{n,} 或者{n,m},表示知道得有左位數和最多位數

    str = 'erabuiababidjabababkjlsdababababkl'
    // 至少得有2個ab,最多匹配3個ab
    str.match(/(ab){2,3}/ig) // => [abab, ababab, ababab]

選擇、組合、捕獲、引用

選擇

選擇是由|標識進行匹配,可以理解為or,如:a|b表示匹配a或者b

str = 'absdfg'
// 匹配s或者d
str.match(/s|d/ig) // => ['s', 'd']

組合(字符集合範圍)

組合在常用的匹配模式中還是常用的,[]用來組合匹配的範圍或者其他

幾個重要概念:

  1. [^..]表示非組合的內容,^在開頭代表不是後面要匹配的內容
  2. [\b]匹配一個退格,不要和 \b混淆了

    // 匹配第一位是字符全是大寫或者全是小寫的字母
    reg1 = /^[A-z]\d*/ig
    reg1.test('s12314') // => true
    
    // 匹配第一位是字母或者數字的,最後一個第一位是字母或者0-2之間的數字,很明顯false
    reg2 = /^[A-z|\d]\d*/ig
    reg2.test('s12314') // => true
    reg2.test('112314') // => true
    reg3 = /^[A-z|0-2]\d*/ig
    reg3.test('312314') // => false
    
    // 匹配第一位不是字母
    reg4 = /^[^A-z]\d*/ig
    reg4.test('s12314') // => false
    reg4.test(';12314') // => true

捕獲

捕獲在匹配表達式中經常看到,他的作用非常強大,使用()一對括號進行修飾,這樣就可以捕獲到匹配到裏面的內容,捕獲經常和引用或者修飾符(*、+...)結合使用

在講用法前,需要補充一下排除捕獲,排除捕獲也很重要很常用,可以用一些需要()但又不需要捕獲後的值,用?:排除捕獲

常見用法:

  1. 使用捕獲獲取具體內容 RegExp 的靜態屬性 $
str = 'I like Java coding'

// 忽略大小寫
reg = /\w*(JAVA)\w*/ig

// 匹配字符
reg.test(str)
// 匹配後,捕獲到的內容會按照捕獲的順序出現在 `RegExp`的靜態屬性上,以`$`開頭
console.log(RegExp.$1) // => Java 
console.log(RegExp.$2) // => undefined ,只有一個捕獲,不存在`$2`屬性

// 使用`replace`方法
str.replace(reg, 'PHP') // => I like PHP coding
str.replace(reg, '【$1】') // => I like 【Java】 coding
str.replace(reg, '$1Script') // => I like JavaScript coding
str.replace(reg, ($, $1) => {
  console.log('匹配到了' + $1)
  return `【${$1}】`
}) // => I like 【Java】 coding  並且會打印 `匹配到了Java`
// 搜索段落標紅關鍵字
str.replace(reg, '<span style="color:red">$1</span>') // => I like <span style='color:red'>Java</span> coding
  1. 匹配表達式結合引用使用

引用其實和前面講到的RegExp的靜態屬性類似,只不過引用是在匹配表達式中使用,寫法不一樣。在匹配表達式中使用 \1 \2...斜槓加一個數字的形式引用所有引用的第一個,類似 $1、$2...

引用的順序與()的使用順序相關聯,規則:由左到右,由外到內,遇到排除捕獲跳過當前()

str = 'qaabbccddq'
reg = /(\w)\1/ig
str.match(reg) // => ['aa', 'bb', 'cc', 'dd']
str = 'qaaaabbbb11q'
reg = /(\w\w)\1(\d)\2?/ig
str.match(reg) // => ['bbbb11']

// 這裏注意是 `\3` 而不是 `\2`
reg1 = /(\w\w)\1((\d)\3)?/ig
str.match(reg1) // => ['aaaa', 'bbbb11']

// 這裏注意使用了 `?:` 排除捕獲,所以是 `\2` 而不是 `\3`
reg2 = /(\w\w)\1(?:(\d)\2)?/ig
str.match(reg1) // => ['aaaa', 'bbbb11']

引用

引用在匹配表達式中使用 \1 這種以 斜槓和數字 結合的方式使用,需要注意的是,引用的數字和使用 ()的規則相關,規則為:由左到右,由外到內,遇到排除捕獲跳過當前(),這一點很重要

關於引用的用法,詳見前面 捕獲小結的使用方法即可

斷言

通常我們要匹配的內容後面要跟着指定的內容,常見的斷言x(?=y)x(?!y)(?<=y)x(?<!y)x

前瞻斷言

前瞻斷言主要是x(?=y)x(?!y)x(?=y)表示匹配後面緊跟着y的x,x(?!y)表示匹配後面不是y的x

str = '1 candy costs 50€'

// 假如要匹配後面是 `€` 的數字
str.match(/\d+(?=€)/) // => [50]

// 假如要匹配後面不是 `€` 的數字
str.match(/\d+(?!€)/) // => [1]

後瞻斷言

後瞻斷言主要是(?<=y)x(?<!y)x(?<=y)x表示匹配前面緊跟着y的x,(?<!y)x表示匹配前面不是y的x

str = '1 candy costs $50'

// 假如要匹配前面是 `$` 的數字
str.match(/(?<=\$)\d+/) // => [50]

// 假如要匹配前面不是 `$` 的數字
str.match(/(?<!\$)\d+/) // => [1]

貪婪與惰性

正則的量詞通常都是遵循貪婪原則,也就是有量詞的時候,儘量匹配多的。而惰性則和這種原則相反,儘量匹配少,不匹配多。

惰性使用?緊跟在量詞的後面,如:

`\d+?` // 匹配一個數字就行
`\w{1,}?` // 匹配1個字母就行
`\d*?` // 匹配0個數字 => ['', ...]

例子

// 匹配帶 `""`號的水果
str = 'I like "apples" a little more than "oranges"'


// 1.貪婪
// 會發現結果並不理想,原因就是默認是貪婪原則,能匹配多的不匹配少的
// 首先,會匹配到第一個`"`
// 而後匹配下一個`"`,當匹配到`apples"`,還會繼續往後匹配
// 看剩下的能不能匹配到 `.+"`,最後匹配到了`oranges"`(當然前面還有`than "`)
str.match(/".+"/g) // => ["apples" a little more than "oranges"]

// 2.惰性
str.match(/".+?"/g) // => ["apples", "oranges"]

// 3.其他方法
str.match(/"[^"]+"/g) // => ["apples", "oranges"]

RegExp屬性、方法

屬性

常見的屬性:source,global,ignoreCase,multiline,lastIndex等等...

  1. source
    表示正則對象的原表達式內容以字符串的形式輸出,不包含修飾符

    reg = /(\d\s)\1/ig
    
    reg.source // => '(\\d\\s)\\1'
  2. global
    表示是否匹配全部內容,而不是匹配到第一個就停止匹配,返回Boolean類型

    reg = /(\d\s)\1/ig
    
    reg.global // => true
  3. ignoreCase
    表示匹配是否忽略大小寫,返回Boolean類型

    reg = /(\d\s)\1/ig
    
    reg.ignoreCase // => true
  4. multiline
    表示多行匹配,有回車符,換下一行從頭匹配

    reg = /(\d\s)\1/ig
    
    reg.multiline // => false
  5. lastIndex
    在全局匹配時,表示當前匹配的的索引,初始為0,當匹配到結果時,lastIndex值變為最新匹配的位置,如果不能匹配到結果了,然後重0開始

    str = 'day by day'
    
    reg = /\w+/g
    
    reg.exec(str) // => ['day', index: 0, input: 'day by day', groups: undefined]
    reg.lastIndex // => 3
    
    reg.exec(str) // => ['by', index: 4, input: 'day by day', groups: undefined]
    reg.lastIndex // => 6
    
    reg.exec(str) // => ['day', index: 7, input: 'day by day', groups: undefined]
    reg.lastIndex // => 10
    
    reg.exec(str) // => null
    reg.lastIndex // => 0,重新從頭開始

    方法

    常見的方法:exec、test

  6. exec
    一個在字符串中執行查找匹配的RegExp方法,它返回一個數組(未匹配到則返回 null)
    同上lastIndex例子
  7. test
    一個在字符串中測試是否匹配的RegExp方法,它返回 true 或 false。

    str = '1a1aklk2b2bp'
    reg = /(\d\w)\1+/
    reg.test(str) // => true

String常見的正則方法

常用的方法:replacematchmatchAllsearchsplit

  1. replace:字符串替換指定的內容(表達式匹配)
str = 'I like Java coding'

str.replace(/JAVA/i, 'PHP') // => 'I like PHP coding'
str.replace(/(JAVA)/i, '<$1>') // => 'I like <Java> coding'
str.replace(reg, ($, $1) => {
  console.log('匹配到了' + $1)
  return `【${$1}】`
}) // => I like 【Java】 coding  並且會打印 `匹配到了Java`
  1. match:匹配所有符合表達式的內容,返回數組,否則null
str = 'd1sd2asf43d5'

str.match(/\d+/) // => [1]
str.match(/\d+/g) // => [1, 2, 43, 5]
str.match(/\d\b/) // => [5]
str.match(/\d\B/) // => [1, 2, 43]
str.match(/\d\s/) // => null
  1. matchAll:返回匹配正則表達式的迭代器,參數為 RegExp 類型,且必須帶上 修飾符 g,否則報錯;其匹配結果返回一個 RegExpStringIterator 類型的迭代器

    str = 'd1sd2asf43d5'
    
    iterator = str.matchAll(/\d+/g) // => RegExpStringIterator {}
    Object.prototype.toString.call(iterator) // => [object RegExp String Iterator]
    
    // 返回結果基本和迭代器一致,value是類數組
    iterator.next() // => {value: [1], done: false}
    iterator.next() // => {value: [2], done: false}
    iterator.next() // => {value: [43], done: false}
    iterator.next() // => {value: [5], done: false}
    iterator.next() // => {value: undefined, done: true}

迭代器截圖

  1. search:匹配正則表達式,如果匹配成功返回第一個匹配的位置,否則返回-1
str = '1 apple costs $50'

str.search(/(?<=\$)\d+/) // => 15
str.search(/\d+/) // => 0
str.search(/\d[A-z]/) // => -1
  1. split:將以正則匹配的內容進行分割
str = 'skJavaslkdJAVAllksdfJaVAlp'

// 將會以 java 或者 javas 進行分割(不區分大小寫)
str.split(/javas?/i) // => ['sk', 'lkd', 'llksdf', 'lp']

  1. A-Za-z0-9_ ↩
  2. 0-9 ↩
  3. \f\n\r\t\v\u00a0\u1680\u180e\u2000-u200a\u2028\u2029\u202f\u205f\u3000\ufeff ↩
user avatar jingdongkeji 頭像 pulsgarney 頭像 dirackeeko 頭像 Dream-new 頭像 zero_dev 頭像 yuzhihui 頭像 eolink 頭像 munergs 頭像 lovecola 頭像 ldh-blog 頭像 Poetwithapistol 頭像 beckyyyy 頭像
點贊 58 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.