前言
本章介紹正則的擴充套件。有些不常用的知識瞭解即可。
本章原文連結:正則的擴充套件
RegExp 建構函式
從 ES6 開始,如果RegExp
建構函式第一個引數是一個正則物件,並且第二個標誌存在且為標誌引數,將不再丟擲 TypeError
,將使用這些引數建立一個新的正規表示式。原有的正規表示式修飾符將被忽略
const flag = new RegExp(/[0-9]/ig, 'i').flags; // 原有修飾符衛 ig ,被 i 給替代了
console.log(flag); // i
字串有關正規表示式
ES6將之前字串上的四個關於正規表示式的方法全部更改為RegExp
的例項方法,所以現在所有與正規表示式有關的方法,全部定義在RegExp
物件上。
String.prototype.match
呼叫RegExp.prototype[Symbol.match]
String.prototype.replace
呼叫RegExp.prototype[Symbol.replace]
String.prototype.search
呼叫RegExp.prototype[Symbol.search]
String.prototype.split
呼叫RegExp.prototype[Symbol.split]
flags 屬性
RegExp.prototype.flags
屬性 是ES6新增屬性,會返回正規表示式的修飾符。
const SAMPLEREG = /abc/ig;
console.log(SAMPLEREG.flags); // gi
u 修飾符
在ES6中新增了 u
修飾符,表示使用Unicode
碼的模式進行匹配。處理大於\uFFFF
的 Unicode
字元
注意
一旦加上u
修飾符號,就會修改下面這些正規表示式的行為。
- 點字元
對於碼點大於0xFFFF
的 Unicode
字元,點字元不能識別,必須加上u
修飾符。
**Unicode**
** 字元表示法**
新增了使用大括號表示 Unicode
字元,這種表示法在正規表示式中必須加上u
修飾符,才能識別當中的大括號,否則會被解讀為量詞。
- 量詞
使用u
修飾符後,所有量詞都會正確識別碼點大於0xFFFF
的 Unicode
字元。
- 預定義模式
u
修飾符也影響到預定義模式,能否正確識別碼點大於0xFFFF的 Unicode 字元。
i
修飾符
有些 Unicode
字元的編碼不同,但是字型很相近,比如,\u004B
與\u212A
都是大寫的K
- 轉義
沒有u
修飾符的情況下,正則中沒有定義的轉義(如逗號的轉義\,
)無效,而在u模式會報錯。
unicode 屬性
RegExp.prototype.unicode
屬性表明正規表示式帶有"u
" 標誌。 unicode
是正規表示式獨立例項的只讀屬性。
const SAMPLEREG = /abc/u;
console.log(SAMPLEREG.flags); // u
console.log(SAMPLEREG.unicode); // true
Unicode 屬性類
**Unicode property escapes**
ES2018 引入了一種新的類的寫法\p{...}
和`P{...},用於解決 JavaScript 沒有強有效的方式用匹配出不同文字問題。允許正規表示式匹配符合
Unicode` 某種屬性的所有字元。
\p{Unicode屬性名=Unicode屬性值}
// 對於某些屬性,可以只寫屬性名,或者只寫屬性值。
\p{Unicode屬性值}
\p{Unicode屬性名}
// \P 為 \p 取反
\P{Unicode屬性值}
\P{Unicode屬性名}
注意:
這兩種類只對 Unicode
有效,所以使用的時候一定要加上u
修飾符。
\P{…}
是\p{…}
的反向匹配,即匹配不滿足條件的字元。
const SAMPLEREG = /\p{Script=Greek}/u;
SAMPLEREG.test('π'); // true
y 修飾符
y 修飾符的作用
在ES6中新增了 y
修飾符,表示執行“粘性(sticky)”搜尋,匹配從目標字串的當前位置開始。
y
修飾符與g
修飾符相似,都是全域性匹配,後一次匹配從上一次匹配成功的下一個位置開始。
區別是:g
修飾符只要剩餘位置中存在匹配即可;而y
修飾符必須從剩餘的第一個位置開始匹配。
// y修飾符與g修飾符的區別
const SAMPLE = 'abcdabcd';
const SAMPLEREG1 = /abcd/g;
const SAMPLEREG2 = /abcda/y;
console.log(SAMPLEREG1.test(SAMPLE)); // true
console.log(SAMPLEREG2.test(SAMPLE)); // true
console.log(SAMPLEREG1.test(SAMPLE)); // true
console.log(SAMPLEREG2.test(SAMPLE)); // false
注意
實際上,y
修飾符號隱含了頭部匹配的標誌^
。
const SAMPLEREGGY = /ab/gy;
const SAMPLEREGY = /ab/y;
let sample1 = 'ababcabcd'.replace(SAMPLEREGGY, '-');
let sample2 = 'ababcabcd'.replace(SAMPLEREGY, '-');
// 最後一個ab因為不是出現在下一次匹配的頭部,所以不會被替換。
console.log(sample1);
// 只能返回第一個匹配,必須與g修飾符聯用,才能返回所有匹配。
console.log(sample2);
sticky 屬性
RegExp.prototype.sticky
表示是否設定了y
修飾符。sticky
是正規表示式物件的只讀屬性。
const SAMPLEREG = /a/gy;
console.log(SAMPLEREG.sticky); // true
s 修飾符
ES2018 引入s
修飾符,使得.
可以匹配任意單個字元。包括行終止符(line terminator character)。
行終止符
所謂行終止符,就是該字元表示一行的終結。以下四個字元屬於“行終止符”。
U+000A
換行符(\n
)U+000D
回車符(\r
)U+2028
行分隔符(line separator
)U+2029
段分隔符(paragraph separator
)
const SAMPLEREG = /ab.cd/s;
console.log(SAMPLEREG.test('ab\ncd') ); // true
dotAll
上面這種情況被稱為**dotAll**
模式,即點(dot)代表一切字元。正規表示式還引入了一個**dotAll**
屬性
dotAll
屬性返回一個布林值,表明是否在正規表示式中一起使用"s
"修飾符。dotAll
是一個只讀的屬性,屬於單個正規表示式例項。
const SAMPLEREG = /ab.cd/s;
const sample = SAMPLEREG.test('ab\ncd');
console.log(SAMPLEREG.flags); // s
console.log(SAMPLEREG.dotAll); // true
後行斷言
ES2018 引入後行斷言,V8 引擎 4.9 版(Chrome 62)已經支援。
-
先行斷言
x
只有在y
前面才匹配,必須寫成/x(?=y)/
。
比如,只匹配百分號之前的數字,要寫成/\d+(?=%)/
。 -
先行否定斷言
,
x
只有不在y
前面才匹配,必須寫成/x(?!y)/
。
比如,只匹配不在百分號之前的數字,要寫成/\d+(?!%)/
。 -
後行斷言
正好與先行斷言
相反,
x
只有在y
後面才匹配,必須寫成/(?<=y)x/
。
比如,只匹配美元符號之後的數字,要寫成/(?<=\$)\d+/
。 -
後行否定斷言
則與先行否定斷言
相反,
x
只有不在y
後面才匹配,必須寫成/(?<!y)x/
。
比如,只匹配不在美元符號後面的數字,要寫成/(?<!\$)\d+/
。
後行斷言需要先匹配/(?<=y)x/
的x
,然後再回到左邊,匹配y
的部分。順序為先右後左,
// 先行斷言
const sample1 = /\d+(?=%)/.exec('100% of US presidents have been male');
// 先行否定斷言
const sample2 = /\d+(?!%)/.exec('that’s all 44 of them');
console.log(sample1); // 100
console.log(sample2); // 44
// 後行斷言
const sample3 = /(?<=\$)\d+/.exec('Benjamin Franklin is on the $100 bill');
// 後行否定斷言
const sample4 = /(?<!\$)\d+/.exec('it’s is worth about €90');
console.log(sample3); // 100
console.log(sample4); // 90
組匹配
正規表示式的括號表示分組匹配,括號中的模式可以用來匹配分組的內容。
ES2018 引入了具名組匹配(Named Capture Groups),允許為每一個組匹配指定一個名字,既便於閱讀程式碼,又便於引用。
具名組匹配在圓括號內部,模式的頭部新增“問號 + 尖括號 + 組名”(?<year>)
,然後就可以在exec
方法返回結果的groups
屬性上引用該組名。同時,數字序號依然有效。
const sampleUsers = `
姓劉名備字玄德
姓關名羽字雲長
姓張名飛字翼德`;
const SAMPLEREG = /姓(?<surnames>.+)名(?<name>.+)字(?<word>.+)/g;
let result = SAMPLEREG.exec(sampleUsers);
do { console.log(`${result.groups.surnames}${result.groups.name}${result.groups.surnames}${result.groups.word}`);
} while ((result = SAMPLEREG.exec(sampleUsers)) !== null);
/*
* 劉備劉玄德
* 關羽關雲長
* 張飛張翼德
*/
上面的程式碼中:?<xxx>
的作用就是為這個匹配定義一個組名,在匹配的groups
屬性中可以檢視到匹配的組名,這裡可以使用解構賦值直接從匹配結果上為變數賦值。
注意 : 如果要在正規表示式內部引用某個具名組匹配,可以使用
\k<組名>
的寫法
matchAll()
ES2020 增加了String.prototype.matchAll()
方法,可以一次性取出所有匹配。不過,它返回的是一個遍歷器/迭代器(Iterator
),而不是陣列。
const string = 'sample1sample2sample3';
const regex = /sample/g;
for (const match of string.matchAll(regex)) {
console.log(match);
}
// 遍歷輸出
/*
['sample', index: 0, input: 'sample1sample2sample3', groups: undefined]
['sample', index: 7, input: 'sample1sample2sample3', groups: undefined]
['sample', index: 14, input: 'sample1sample2sample3', groups: undefined]
*/