博客 / 詳情

返回

JS中的第二世界--正則表達式

近期的繁忙讓我一直沒有空閒靜下心來好好寫一些文章。好在,所有的忙碌都已過去,願明天更美好。剛過完七夕,帶着歡樂的心情寫下這篇文章。希望讀者能夠喜歡喲~~

你是不是經常遇到正則,卻不知它在説什麼,你是不是就算再項目中看到了,也會選擇一眼略過,你是不是整天忘記搜索什麼,卻不知道有的編輯器搜索支持正則的模糊搜索……

熟悉的旋律縈繞在耳邊,卻早已不是當初的少年。

工作了很久之後,猛然發現之前自己忽略的正則是這麼重要。搜索、查詢、命令行、校驗、截取、替換…………哪樣哪樣都離不開這個小東西。很多人不知道的是,正則在 JavaScript 裏彷彿一個第二世界一樣。看起來簡單,實則有很多讓我們摸不着頭腦的操作。

接下來,讓我們順着本文的介紹,一點兒一點兒深入瞭解正則表達式,知曉正則的強大能力!

1. 創建正則表達式

JavaScript 中創建正則表達式有兩種方式

第一種:構造函數創建

const reg = new Reg('正則表達式')

第二種:字面量創建

const reg = /正則表達式/

2. 使用正則表達式匹配內容

正則匹配分為 精準匹配模糊匹配

精準匹配

只需要我們將需要匹配的內容放入到正則表達式中匹配即可。上手簡單

const reg = /正則表達式/
const str = '這是一篇寫正則表達式是文章'

str.match(reg) // 正則表達式

這裏的正則表達式會精準的搜索到我們要搜索的內容。

模糊匹配

模糊匹配需要用到第三點所描述的內容,這裏我們先看下

const reg = /[\u4e00-\u9fa5]+/
const str = '這是一篇寫正則表達式Regpx的內容'

str.match(reg) // 這是一篇寫正則表達式

這裏的正則表達式會根據我們傳入的規則來匹配要搜索的內容。注意:這裏的\u4e00-\u9fa5表示的是中文範圍

\u4e00 表示的是字符集中第一個中文漢字

\u9fa5 表示的是字符集中最後一個中文漢字

ok,知道了怎麼創建和使用正則,接下來我們來真正瞭解一下他吧。

3. 正則表達式介紹

通曉元字符的同學可跳過此小節,直奔第四小節查看。主要內容有 . | \ * + ? () [] {} ^ $ 幾種字符使用方法,含義和注意事項,內置匹配項,貪婪和惰性匹配

首先介紹一下前瞻條件,正則可以根據自己的設置來進行多行和全局,局部,忽略大小寫等匹配模式,分為:

  • m :多行匹配
  • g: 全局匹配
  • i: 忽略大小寫

接下來的內容,就可以看到我們來使用它

3.1 元字符

元字符是正則表達式中最基礎的部分,它代表了我們可以查詢的內容。come on baby。讓我們來認識一下他們吧。

  • . :匹配除去 \n 之外的所有內容
const reg = /.*/
const str1 = 'abc'

// 在str2中添加 \n 換行符
const str2 = 'a\nb\nc'

str1.match(reg) // abc
str2.match(reg) // a
  • |:在正則中進行邏輯判斷,(説的比較抽象,我們來看下例子)
const reg = /正則|表達/
const str1 = '正則表達式'
const str2 = '表達式'

str1.match(reg) // 正則
str2.match(reg) // 表達

解釋:

如果可以匹配到 | 前邊的內容,則匹配前邊的。

匹配不到前邊的,但是可以匹配後邊的內容,則匹配後邊的

  • 反斜槓 \ :表示轉義
const reg = /\./
const str = 'a.b.c.d'

str.match(reg) // .

可以匹配除 \n 外任意字符的點 . 在使用 \ 轉義之後只能匹配到 . 這個字符

需要注意的點:

在有的編程語言中,匹配 \\ 需要四個反斜槓 -- \\\\。但是在 javascript 中,只需要兩個。

const reg = /\\/
const str = '\\'

str.match(reg) // \\
  • 星號 *:需要匹配的內容出現連續零次或多次
const reg = /a*/
const str1 = 'aaabbcccddd'
const str2 = 'bbcccddd'
reg.test(str1) // true
reg.test(str2) // true
  • 加號 +:需要匹配的內容出現連續一次或多次
const reg = /a+/
const str1 = 'aaabbcccddd'
const str2 = 'bbcccddd'
reg.test(str1) // true
reg.test(str2) // false
  • 問號 ?:需要匹配的內容出現連續零次或一次
const reg = /a?/
const str1 = 'aaabbcccddd'
const str2 = 'bbcccddd'

reg.test(str1) // true
reg.test(str2) // true
  • 小括號(): 表示組,小括號中的內容表示一組,整體進行匹配。
const reg = /(abc)/
const str1 = 'abab'
const str2 = 'abcab'
reg.test(str1) // false
reg.test(str2) // true

拓展:

使用小括號包裹的匹配內容,可在後續的正則中通過\ + 序號的方式繼續調用。有幾個小括號就有幾個序號。序號從1開始

const reg = /(abc)\1/  // 相當於 const reg = '(abc)abc'
const str = 'abcabc'

str.match(reg)

//   /(a)(b)(c)\1\2/ ---> /(a)(b)(c)ab/
  • 中括號 []:表示範圍,括號中的內容只要有一個匹配到就可以
const reg = /[abc]/
const str = "efg"
const str1 = 'aef'

reg.test(str) // false
reg.test(str1) // true

注意:

中括號中可以使用 - 來表示連續的範圍

/[0-9]/   0123456789
/[a-z]/   全部英文小寫
/[A-Z]/   全部英文大寫
  • 大括號 {m,n}:表示出現次數

    m 表示最少出現多少次 n表示最多出現多少次,n可省略,表示無限次

const reg = /a{1,4}/ // a最少出現1次,最多出現4次
const str = 'aaaaa'

reg.test(str) // true

拓展:

{0,} 相當於 *

{1,} 相當於 +

{0,1}相當於 ?

  • 開頭^:只匹配開頭內容
const reg = /a^/
const str = 'abc'
const str1 = 'bac'

reg.test(str) // true
reg.test(str1) // false

注意:

此符號用在 [] 中,表示取反操作

const reg = /[^abc]/g 
const str = '123'
str.match(reg) // ['1', '2', '3']

這裏只能取到不是 abc 的內容

  • 結尾$: 只匹配結尾
const reg = /a$/
const str = 'abc'
const str1 = 'cba'

reg.test(str) // false
reg.test(str1) // true

3.2 內置匹配符

\d:數字

\D:非數字

\s:空格

\S:非空格

\w:數字字母下劃線

\W:非數字字母下劃線

\b:字符邊界

\B:非字符邊界

const str = 'hello word! 520'

console.log(str.replace(/\d/g, '*')) // hello word! ***
console.log(str.replace(/\D/g, '*')) // ************520
console.log(str.replace(/\w/g, '*')) // ***** ****! ***
console.log(str.replace(/\W/g, '*')) // hello*word**520
console.log(str.replace(/\b/g, '*')) // *hello* *word*! *520*
console.log(str.replace(/\B/g, '*')) // h*e*l*l*o w*o*r*d!* 5*2*0
console.log(str.replace(/\s/g, '*')) // hello*word!*520
console.log(str.replace(/\S/g, '*')) // ***** ***** ***

3.3 貪婪和惰性

貪婪匹配是最多情況下匹配內容

惰性匹配是最少情況下匹配內容

先看栗子

const reg = /a[bc]*c/g  // 貪婪
const str = 'abcbcbc'

console.log(str.match(reg)) // [ 'abcbcbc' ]

const reg1 = /a[bc]*?c/g // 惰性
console.log(str.match(reg1)) // [ 'abc' ]

解釋:

  • 貪婪: 由於 abcbcbc 一直到字符串最後都符合正則的規則,所以一直匹配到不符合位置
  • 惰性:只匹配一個符合規則的就返回,不繼續匹配。

4. 正則進階操作

4.1 具名組匹配

JavaScript 中提供了組匹配的能力。可以同過自定義的組名來獲取匹配內容.

組的固定格式為 ?<組名>。然後通過獲取結果的groups.具體的名稱 來獲取

const reg = /(?<name>[a-zA-Z]{3,6})(?<age>\d{2})/
const str = 'xiaoming23'

str.match(reg).groups.name // xiaoming
str.match(reg).groups.age // 23

4.2 位置匹配

  • ?=搜索內容:查看右側內容是否符合條件, 查看後綴是否為要搜索的內容。
const reg = /(?=abc)/
const str = 'efabcefabc'

str.replace(reg, '*') // ef*abcef*abc

解釋:

匹配 後綴為abc 這個字符的位置

  • ?!搜索內容:查看右側內容是否不符合條件,查看後綴是否為要搜索的內容
const reg = /(?!abc)/
const str = 'efabcefabc'

str.replace(reg, '*') // *e*fa*b*c*e*fa*b*c*

解釋:

匹配 後綴不為 abc 這個字符的位置

  • ?<=搜索內容:查看左側內容是否符合條件,查看前綴是否是要搜索的內容
const reg = /(?<=abc)/
const str = 'efabcefabc'

str.replace(reg, '*') // efabc*efabc*

解釋:

匹配 前綴為abc 這個字符的位置。

  • ?<!搜索內容:查看左側內容是否不符合條件
const reg = /(?<!abc)/
const str = 'efabcefabc'

str.replace(reg, '*') // *e*f*a*b*ce*f*a*b*c

解釋:

匹配 前綴不為abc 這個字符的位置。

第四小節總結:

  1. 組匹配 ?<組名>
  2. 匹配後綴 ?=?!
  3. 匹配前綴 ?<=?<!

5. 正則和字符串方法介紹

5.1 字符串方法

下述方法都支持正則匹配方式來操作。

  • replace(reg, 替換的內容 或者 一個操作函數) 替換

    此方法的第二個參數是比較神奇的,可以接收要替換的內容,也可以接受一個函數。

    • 為替換內容(1)
    const str = 'abc'
    str.replace(/a/, '*') // *bc
    • 為替換內容(2)

      可使用 $1,$2………… 等變量作為匹配到的組的內容。

    const str = '123abc'
    str.replace(/(a)(b)(c)/, '$1$3$2') // 123acb

    説明:

    $1 代表 (a) 所匹配到的內容

    $2 代表 (b) 所匹配到的內容

    $3 代表 (c) 所匹配到的內容

    替換的時候掉到順序替換,則輸出 123acb 這個值

    • 為函數
    const str = 'abc'
    str.replace(/(a)/, (source, $1, index) => {
      console.log(source, $1, index) // abc  a  0
      return *
    }) // *ab
    
    str.replace(/(b)(c)/, (source, $1, $2, index) => {
        console.log(source, $1, $2, index) // abc 
        return $1 + 1
    }) // a11

    説明:

    函數參數接收的參數,第一個source 為字符串本身,最後一個index為查找到的第一個索引值。中間的 $1, $2, $3……………… 為正則表達式裏小括號的數量。返回值的作用是作為替換內容替換原字符串。

  • match 搜索

    根據正則的規則匹配字符串

    const reg = /[abc]/g
    const str = 'abc'
    
    str.match(reg) // [ 'a', 'b', 'c' ]
  • split 切割

    const str = 'abcabcabc'
    
    str.split(/ca/) //[ 'ab', 'b', 'bc' ]

    此方法還可接收第二個參數。length 設置返回的數組長度

    const str = 'abcabcabc'
    
    str.split(/ca/, 2) //[ 'ab', 'b' ]

5.2 正則方法

  • test 查看字符串是否符合正則規則

    const reg = /abc/
    const str = 'abc'
    const str1 = 'ab'
    
    reg.test(str) // true
    reg.test(str1) // false
  • exec 根據正則的規則匹配字符串。同 match

    const reg = /abc/
    const str = 'abc'
    const str1 = 'ab'
    
    reg.exec(str) // 'abc'
    reg.exec(str1) // null

6. 實戰篇

理解了內容之後怎麼也得練練手啊,此內容給大家準備了幾個常見但是不太好理解的正則,請大家練手。

6.1 匹配 html 標籤 (包含標籤中的內容)

首先,我們來創建個字符串

const html = '<div></div>'

現在我們來抒寫一下可描述標籤的正則表達式。有以下幾個特徵:

  1. < 開頭
  2. 標籤名為英文字符
  3. </標籤名> 結尾
第一版表達式
const reg = /<(\w+)><\/(\1)>/g

// 驗證一下
html.match(reg) // '<div></div>'

\1引用上一個分組的正則規則上面的內容已經説過。

看我們的驗證結果,完美。但是,有個問題。我們的標籤一般都不在一行內書寫,標籤之間會有 \n 來標識換行。ok,讓我們修改一下 html 字符串

const html = `<div>

</div>`

// 再次驗證
html.match(reg) // null

😰完了,失敗了。

不要着急,我們只匹配了標籤,並沒有匹配到標籤中的內容。由於 . 並不能匹配到 \n 所以我們使用其他條件來匹配。

第二版表達式
const reg = /<(\w+)>([\s\S]*)<\/(\1)>/g 

// 驗證
html.match(reg) // '<div>\n\n</div>'

(^o^)/,成功。

這裏我們可以看到。使用 [\s\S] 可以匹配到 \n。因為 [\s\S] 代表的是空格和非空格。相同的用法還有 [\w\W]、[\b\B]、[\d\D]

第三版表達式

html 除了雙標籤還有單標籤,下面我們來看下單標籤的驗證。單標籤的開始跟雙標籤一樣。但是結束不一樣,單標籤是以/>結束。不説了,先寫一下

let reg = /<(\w+)/    // 相同的開頭

// 結束規則書寫
reg = /<(\w+)\/>/ 

注意,這裏,單標籤中的內容,我們不需要用 [\s\S] 這種方法驗證,會有其他問題,因為我們需要匹配的是屬性,匹配到換行符結束。所以下面這種寫法就可以。

reg = /<(\w+)([^>]*)\/>/

[^>] 表示只要不是結束符號的,都符合條件。* 表示出現零次或多次

不説了,我們來驗證下

const html = '![]()'
html.match(reg) // ![]()

😄,成功。

結合版(不是終極版)

結合版的正則表達式我們需要用到元字符中的|來作為分支判斷

const reg = /<(\w+)>(([\s\S]*)<\/(\1)>)|(([^>]*)\/>)/mg
// (([\s\S]*)<\/(\1)>) 雙標籤
// (([^>]*)\/>)單標籤

説明:m 的作用是來表示匹配多行。g 的意思是表示全局匹配

驗證一下

const html = '<div title="1"></div><p></p>![](asfs)<br />'

html.match(reg)
// [ '<p></p>', '![](asfs)', '<br />' ]

成功!!

雖然這次的匹配成功了,但是這個表達式還是有很多問題,期待讀者來完善喲。

6.2 實現數字千分位

同樣,我們來分析一下需求。千分位是每隔三個數字,加上一個逗號。首先,我們先創建一個數字的字符串

const str = '12345678'

查看後綴是否是三個數字,我們使用 ?= 來做,它的作用就是查看後綴是否符合規則。先來創建正則

第一版
const reg = /(?=\d{3})/g

因為需要匹配整個數字,所以我們用到了 g 來表示全局匹配。好,正則創建完成,驗證一下。

str.replace(reg, ',') // ,1,2,3,4,5,678

整個結果,好像有問題啊。

不着急,繼續向下看,由於我們要數字三個一對的出現,所以這裏我們需要添加出現一次或多次的校驗。使用+

第二版
const reg = /(?=(\d{3})+)/g

先不急着驗證,因為還沒有完成。由於每三個字符是一個節點,所以這裏還需要用 $ 來表示查找結束。

第二版改進
const reg = /(?=(\d{3})+$)/g

驗證:

str.replace(reg, ',') // 12,345,678

貌似是成功了,多來驗證幾次。

…………

驗證到這個字符串時出現問題。

const str = '123456789'
str.replace(reg, ',') // ,123,456,789

這裏我們看到,數字正好是九位,所以開始的 123 也符合條件。所以需要吧開頭禁止掉。使用 ?!這種方式

第三版
const reg = /(?!^)(?=(\d{3})+$)/g

再次驗證:

str.replace(reg, ',') // 123,456,789

7. 總結

好了,本次的內容我們就先分享到這裏了,期望你能從這篇文章中真正瞭解到正則表達式。本篇文章一共分享了一下幾個內容

  • 元字符
  • 位置匹配
  • 字符串和正則表達式方法講解
  • 實戰操作

正則的使用方式千變萬化。所謂能力越大,危害就越大,只有真正掌握了它,才能在實際應用中得心應手,否則容易造成不小的禍端。

那麼,再見了,親愛的讀者朋友們,期待下次相遇~~

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.