javascript

JavaScript 正規表示式

正規表示式是用於匹配字串中字元組合的模式。正規表示式的模式規則是由一個字元序列組成的。包括所有字母和數字在內,...

>

正規表示式是用於匹配字串中字元組合的模式。正規表示式的模式規則是由一個字元序列組成的。包括所有字母和數字在內,大多數的字元都是直接按照直接量描述待匹配的字元。除此之外,正規表示式還有其他特殊語義的字元,這些字元不按照特殊含義進行匹配。

建立正規表示式

JavaScript 中的正規表示式用 RegExp 物件表示,有兩種建立方式。

1. 直接量語法建立

正規表示式直接量定義為包含在一對斜槓( / )之間的字元。

2. 構造函建立

可以通過 RegExp() 建構函式可以實現動態建立正規表示式。 RegExp 的第二個引數是可選的。

new RegExp(pattern [, flags])
RegExp(pattern [, flags])

其中 pattern 可以是字串或者正則字面量。當 pattern 是字串時,需要常規的字元轉義規則,必須將 \ 替換成 \\ ,比如 /\w+/ 等價於 new RegExp("\\w+")

直接量字元

正規表示式中所有字母和數字都是按照字面含義進行匹配的,其他非字母的字元需要通過反斜槓( \ )作為字首進行轉移,如 \n 匹配換行符。這些字元為 直接量字元(literal characters) 。這些字元都是精確匹配,每一個字元都只能匹配一個字元。

在正規表示式中,有一些標點符號具有特殊含義,他們是: ^ $ . * + ? = ! : | \ / ( ) [ ] { } 如果需要在正規表示式中與這些直接量進行匹配,必須使用字首 \

如果不記得哪些標點需要反斜槓轉義,可以在每個標點符號前都加上反斜槓。

字元類

如果不想匹配某一個特定的字元而是想匹配某一類字元,則需要使用字元類。

通過將直接量字元放入方括號內,可以組成字元類(character class)。一個字元類可以匹配它所包含任意 一個 字元。如 [abc] 可以匹配 a,b,c 中任意一個字元。

使用 ^ 作為方括號中第一個字元來定義否定字符集,它匹配所有不包含在方框括號內的字元。[^] 可以匹配任意字元。

字元類可以使用連字元來表示字元範圍。比如匹配小寫字母 [a-z] ,匹配任何字母和數字可以用 [a-zA-Z0-9]

一些常用的字元類,在 JavaScript 中有特殊的轉義字元來表達它們。

字元 匹配
[...] 方括號內任意字元
[^...] 不在方括號內任意字元
. 除了換行符和其他 Unicode 行終止符之外的任意字元
\w 等價於 [a-zA-Z0-9_]
\W 等價於 [^a-zA-Z0-9_]
\s 任何 Unicode 空白符
\S 任何非 Unicode 空白符的字元
\d 等價於 [0-9]
\D 等價於 [^0-9]
[\b] 退格直接量,與退格鍵 \u0008 匹配,注意不同於 \b

方括號內也可出現轉義字元,如 [\d\s] 表示匹配任意空白符或數字。

重複

當一個模式需要被多次匹配的時候,正規表示式提供了表示重複的正則語法。

字元 含義
{n,m} 匹配前一項至少 n 次,但不能超過 m 次
{n,} 匹配前一項至少 n 次
{n} 匹配前一項 n 次
? 匹配前一項 0 次或 1 次,等價於 {0,1}
+ 匹配前一項 1 次或多次,等價於 {1,}
* 匹配前一項 0 次或多次,等價於 {0,}

貪婪和非貪婪的重複

上面所有的重複都是「貪婪的」匹配,也就是匹配儘可能多的字元。如 /a+/ 匹配 'aaaa' 時,它會匹配 'aaaa'

如果想要儘可能少的匹配,只需要在重複的標記後加一個問號( ? )即可。如 /a+?/ 匹配'aaaa' 時,它會匹配 'a'

注意:正規表示式的模式匹配總會尋找字串中第一個可能匹配的位置,這意味這 /a+?b/ 匹配 'aaab' 時,匹配到的是 'aaab' 而不是 'ab'

選擇、分組和引用

選擇

字元 | 用於分隔供選擇的模式,匹配時會嘗試從左到右匹配每一個分組,直到發現匹配項。如/ab|bc|cd/ 可以匹配字串 'ab''bc''cd'

分組

圓括號可以把單獨的項組合成子表示式,以便可以像一個獨立的單元用 |*+ 或者 ?對單元內的項進行處理。

引用

帶圓括號的表示式的另一個用途是允許在同一個正規表示式的後面引用前面的子表示式。通過\ 後面加數字實現。 \n 表示第 n 個帶圓括號的子表示式。表示引用前一個表示式所匹配的文字。 因為子表示式可以巢狀,所以根據子表示式左括號的位置進行計數。

例,能匹配 1999-01-01 或 1999/01/01 的正則: /\d{4}([-//])\d{2}\1\d{2}/

具名引用

使用 (?<name>...) 的語法來為分組命名,並通過 \k<name> 在後面的正規表示式中引用。如上面的正則可以改寫為: /\d{4}(?<separator>[-//])\d{2}\k<separator>\d{2}/

忽略引用

如果只想用圓括號來表示子表示式,而不希望生成引用,可以使用 (?:) 來進行分組。例, /(?:a)(?:b)(c)/\1 將表示 (c) 所匹配的文字。

指定匹配位置(錨元素)

有一些正規表示式的元素 不用來匹配實際的字元,而是匹配指定的位置 。我們稱這些元素為正規表示式的錨。

正規表示式中的錨字元包括:

  • ^ 用來匹配字串的開始,多行檢索時匹配一行的開頭。
  • $ 用來匹配字串的結束,多行檢索時匹配一行的結尾。
  • \b 用來匹配單詞的邊界,就是 \w\W 之間的位置,或者 \w 和字串的開頭或結尾之間的位置。
  • \B 匹配非單詞邊界的位置。

例: /\bJava\b/ 可以匹配 Java 卻不匹配 JavaScript

任意正規表示式都可以作為錨點條件。

先行斷言

(?=pattern)` 它表示一個位置,該位置 **之後** 的字元能 **匹配** `pattern` 。如 `/\d+(?=%)/` 匹配字串 `'100%'` 中的 `'100'` 但是不匹配 `'100。'

負向先行斷言

(?!pattern) 它表示一個位置,該位置 之後 的字元能 不匹配 pattern

後行斷言

(?<=pattern) 它表示一個位置,該位置 之前 的字元能 匹配 pattern 。例, /(?<=\$)\d+/ 匹配 '$100' 但是不匹配 '¥100'

負向後行斷言

(?<!pattern) 它表示一個位置,該位置 之前 的字元能 不匹配 pattern

修飾符

在正規表示式的第二條斜線之後,可以指定一個或多個修飾符, /pattern/g

常用修飾符:

  • i 執行不區分大小寫的匹配。
  • g 全域性匹配。
  • m 多行匹配模式。
  • y 「粘連」(sticky)修飾符。y修飾符的作用與g修飾符類似,也是全域性匹配,後一次匹配都從上一次匹配成功的下一個位置開始。不同之處在於,g修飾符只要剩餘位置中存在匹配就可,而y修飾符確保匹配必須從剩餘的第一個位置開始,這也就是「粘連」的涵義。
  • s 表示點(.)可以表示任意字元,不設定的話,四個位元組的 UTF-16 字元和行終止符不能用 . 表示。
  • u 開啟 「Unicode 模式」,用來正確處理大於 \uFFFF 的 Unicode 字元。也就是說,會正確處理四個位元組的 UTF-16 編碼。

通過 RegExp.prototype.flags 可以獲得正則修飾符的字串。 /pattern/ig.flags 返回"gi"

字串的正則方法

String.prototype.search(regexp|substr)

返回第一個和引數匹配的子串的起始位置。沒有匹配子串返回 -1

如果引數不是正規表示式,將會通過 RegExp 建構函式轉換成正規表示式。它會忽略正則的修飾符 g

String.prototype.replace(regexp|substr, newSubStr|function)

第一個引數同 search ,查詢指定子串。如果第二個表示式是字串,將把第一個引數匹配的子串替換為 newSubStr 。如果在替換字串中出現了 $ 加數字, replace 將用與指定的子表示式相匹配的文字來替換這些字元。

例,單書名號包裹文字改為書名號。 '<JavaScript>和<正規表示式>'.replace(/<([^_]*?)>/g, '《$1》') 會得到 "《JavaScript》和《正規表示式》"

使用字串作為引數時替換字串可以插入下面的特殊變數名:

  • $$ 插入一個 "$"
  • $& 插入匹配的子串。
  • `$`` 插入當前匹配的子串左邊的內容。
  • $' 插入當前匹配的子串右邊的內容。
  • $n 假如第一個引數是 RegExp物件,並且 n 是個小於100的非負整數,那麼插入第 n 個括號匹配的字串。提示:索引是從1開始

使用函式作為第二個引數

function replacer(match, p1, p2, p3, offset, string) { }
// match        匹配的子串。
// p1,p2, ...   假如replace()方法的第一個引數是一個RegExp 物件,則代表第n個括號匹配的字串。
// offset       匹配到的子字串在原字串中的偏移量。子串首字母下標。
// string       被匹配的原字串。

例,下劃線命名轉駝峰命名。 'a_simple_name'.replace(/_([a-z])/g, (m, p1) => p1.toUpperCase()) 將得到 "aSimpleName"

String.prototype.match(regexp)

引數 regexp 為一個正規表示式物件。如果傳入一個非正規表示式物件,則會隱式地使用 new RegExp(obj) 將其轉換為一個 RegExp

如果 regexp 沒有設定修飾符 g ,則僅返回第一個完整匹配及其相關的捕獲組(Array),返回陣列第一個字元是匹配字串,餘下的元素是正規表示式中圓括號括起來的子表示式。在這種情況下,返回的專案將具有如下所述的其他屬性(groups: 一個捕獲組陣列 或 undefined (如果沒有定義命名捕獲組)。index: 匹配的結果的開始位置。input: 搜尋的字串。),或者未匹配時返回 null

如果使用 g 標誌,則將返回與完整正規表示式匹配的所有結果,但不會返回捕獲組,或者未匹配時返回 null

'196.168.0.1'.match(/(\d+)(?=.|$)/) // (?=.|$) 先行匹配 匹配 . 或者字串結尾
// (2) ["196", "196", index: 0, input: "196.168.0.1", groups: undefined]
'196.168.0.1'.match(/(?<num>\d+)(?=.|$)/) // (?<name>) 具名引用 見上文
// (2) ["196", "196", index: 0, input: "196.168.0.1", groups: {num: "196"}]
'196.168.0.1'.match(/\d+(?=.|$)/g)
// (4) ["196", "168", "0", "1"]

String.prototype.split([separator[, limit]])

separator 指定表示每個拆分應發生的點的字串,可以是一個字串或正規表示式。如果空字串( "" )被用作分隔符,則字串會在每個字元之間分割。

limit 一個整數,限定返回的分割片段數量。

例, '張三;李四,王五|趙六'.split(/[;\|,]/) // (4) ["張三", "李四", "王五", "趙六"]

RegExp 的屬性

  • flags 會返回正規表示式的修飾符。
  • 表示對應修飾符是否存在的只讀布林值, global (表示是否帶有修飾符 g ), ignoreCasei ), multilinem ), stickyy ), dotAlls ), unicodeu
  • source 只讀字串,包含正規表示式的文字。
  • lastIndex 可讀/寫整數。如果帶有 g 修飾符,這個屬性儲存在整個字串中下一次檢索的開始位置。這個屬性會被 exec()test() 方法用到。

RegExp 的方法

exec()

如果沒有找到任何屬性,將返回 null ,如果找到匹配返回一個數組,該陣列第一個元素是相匹配的字串,餘下的元素是與圓括號內的子表示式相匹配的子串。

當呼叫 exec() 的正規表示式具有修飾符 g 時,它將把當前正規表示式物件的 lastIndex 屬性設定為緊挨著匹配子串的字元位置。

注意即使兩次匹配的不是同一個字串, lastIndex 還是會連續生效的。

let reg = /\d+/g;
reg.exec('25*10=250'); // ["25", index: 0, input: "25*10=250", groups: undefined]
reg.lastIndex; // 2
reg.exec('666'); // ["6", index: 2, input: "666", groups: undefined]
reg.lastIndex; // 3

test()

呼叫 test()exec() 等價,當 exec() 返回結果不是 nulltest() 返回 true ,否則返回 false

String 的方法不會用到 lastIndex 屬性。

Facebook Profile photo
Written by Nat
This is the author box. A short description about the author of this article. Could be their website link, what they like to read about and so on. Profile