「若是你有一個問題想到能夠用正則來解決,那麼你如今有兩個問題了。」 🤷♀️html
正則表達式是用於匹配字符串中字符組合的模式。git
/ab+c/g
new RegExp("ab+c","g")
有 6 個方法可使用正則表達式es6
str.search(regexp)正則表達式
str.match(regexp) 返回一個數組數組
str.replace(regexp|substr, newSubStr|function)markdown
str.split(separator) 分隔符 separator 包括 str|regexpapp
str.matchAll() - ES2020 新增ide
str.replaceAll() - ES2020 新增函數
let str = "hello world!";
/world/.test(str); // true
let str = "hello world!";
str.search(/world/); // 6
複製代碼
若想知道更多返回信息(然而執行比較慢),可以使用 exec() 方法 ⬇️ ⬇️ ⬇️oop
// 返回結果相同
let str = "hello world world!";
/world/.exec(str);
let str = "hello world world!";
str.match(/world/);
// 會返回匹配的全部結果
let str = "hello world world!";
str.match(/world/g);
複製代碼
正則表達式由簡單字符 + 特殊字符組成
正則表達式有六個可選參數 (flags) 容許全局和不分大小寫搜索等
語法:
let regExp = /pattern/flags;
let regExp = new RegExp("pattern", "flags");
let str = "Hello World!";
/world/i.test(str); // true
複製代碼
在正則表達式中具備特殊意義的專用字符,能夠分爲: 特殊字符、量詞、範圍/組、斷言、Unicode 屬性轉義。
1. 特殊單字符
.
匹配任意字符(除換行符外)\d
匹配數字 digit => [0-9]\D
匹配非數字 => [^0-9]\w
匹配一個字符 word(包括字母數字下劃線) => [A-Za-z0-9_]\W
匹配非字符 => [^a-za-z0-9_]\s
匹配空白字符 space,包括空格、製表符、換頁符和換行符\S
匹配非空白字符\b
匹配一個單詞的邊界 boundary\r
匹配回車符\n
匹配換行符\uhhhh
匹配十六進制數表示的 Unicode 字符\u{hhhh}
匹配十六進制數表示的 Unicode 字符(ES6 新增寫法,須要設置 u 標誌)let str = "He played the King in a8 and she moved her Queen in c2.";
str.match(/\w\d/g); // ["a8","c2"]
複製代碼
// 匹配 Unicode 字符
let str = "happy 🙂, confused 😕, sad 😢";
let reg = /[\u{1F600}-\u{1F64F}]/gu;
str.match(reg); // ['🙂', '😕', '😢']
複製代碼
// 匹配中文字符 [\u4e00-\u9fa5]
let str = "123我是456中文";
let reg = /[\u4e00-\u9fa5]/g;
str.match(reg); // ["我", "是", "中", "文"]
複製代碼
2. 量詞
*
匹配 0 次以上(0+ 即有沒有都行) => {0,}
+
匹配 1 次以上 (1+ 即至少一次)=> {1,}
?
匹配 0 或 1 次(可選,可能有可能沒有,有點像 TS 的可選)=> {0,1}
{n}
匹配字符恰好出現 n 次
{n,m}
至少 n 次,最多 m 次
{n,}
至少出現了 n 次
// 匹配規則:一個或多個字符 和 一個空格,全局匹配,忽略大小寫
let re = /\w+\s/gi;
"fee fi fo fum".match(re); // ["fee ", "fi ", "fo "]
複製代碼
3. 範圍 Range / 組 group
[xyz]
字符集合,匹配方括號中的任意字符, 破折號(-)
能夠指定範圍[^xyz]
反向字符集,匹配任何沒有包含在方括號中的字符x|y
匹配 x 或 ylet str = "The Caterpillar and Alice looked at each other";
let reg = /\b[a-df-z]+\b/gi;
str.match(reg); // ["and", "at"]
複製代碼
(x)
1. 分組 2. 捕獲,匹配 x 並記住匹配項,後續經過 \n
來引用第 n 個捕獲的組,替換時使用 $n
來指代。(?:x)
非捕獲括號,匹配的子字符串不會被記住,能夠節省性能let reg = /(apple) (banana) \1 \2/;
"apple banana apple banana apple banana".match(reg);
複製代碼
let reg = /(\w+)\s(\w+)/;
let str = "John Smith";
str.replace(reg, "$2 $1"); // "Smith, John"
複製代碼
4. 斷言-主要是對邊界的判斷
^
匹配輸入的開始(注意:字符集合[^xyz]
中表示反向)
$
匹配輸入的結束
\b
匹配一個單詞的邊界
x(?=y)
先行斷言,匹配 x (僅當後面爲 y) 如: /Jack(?=Sparrow)/
匹配 Jack
(?<=y)x
後行斷言(ES2018),匹配 x (僅當前面爲 y) /(?<=Jack)Sparrow/
匹配 Sparrow
let str = "https://xxx.xx.com/#/index?type=xx&value=xxx";
let reg = /(?<=\?).+/g;
str.match(reg); // ['type=xx&value=xxx']
// 條件過濾
let oranges = ["ripe orange A", "green orange B", "ripe orange C"];
oranges.filter((item) => item.match(/(?<=ripe )orange/)); // ["ripe orange A", "ripe orange C"]
複製代碼
x(?!y)
先行否認斷言,匹配 x (僅當後面不爲 y) /Jack(?!Sparrow)/
(?<!y)x
後行否認斷言(ES2018),匹配 x (僅當前面不爲 y) /(?<!Jack)Sparrow/
下面主要是一些(ES6 新增)修飾符與對應的屬性
let regExp = /ab*/g;
regExp.exec("abbcdefabh"); // ['abb',index:0]
regExp.lastIndex; // 3
// 繼續匹配
regExp.exec("abbcdefabh"); // ['ab',index:7]
regExp.lastIndex; // 9
// 再繼續匹配
regExp.exec("abbcdefabh"); // null
regExp.lastIndex; // 0
複製代碼
有了上述特性,exec() / test () 方法可對字符串進行循環匹配(查找出全部匹配)
let reg = /ab*/g;
let str = "abbcdefabh";
let arr = [];
while ((arr = reg.exec(str)) !== null) {
console.log(arr, reg.lastIndex);
}
// 對比 match ,只會返回匹配到的結果
str.match(reg); // ['abb','ab']
複製代碼
let regExp = /ab*/y;
regExp.exec("abbcdefabh"); // ['abb',index:0]
regExp.lastIndex; // 3
// 繼續匹配
regExp.exec("abbcdefabh"); //null
regExp.lastIndex; // 0
regExp.sticky; // true 表示設置了y修飾符
複製代碼
理解: y 修飾符號隱含了頭部匹配的標誌。y 修飾符的設計本意,就是讓頭部匹配的標誌^在全局匹配中都有效。
\uhhhh
匹配十六進制數表示的 Unicode 字符)/^\uD83D/.test('\uD83D\uDC2A') // true "\uD83D\uDC2A"表明一個字符
/^\uD83D/u.test('\uD83D\uDC2A') // false
let r = /hello/u;
r.unicode; // true
複製代碼
.
匹配任意字符(除換行符外).
能夠匹配任意單個字符,稱爲 dotAll 模式。/foo.bar/.test("foo\nbar"); // false
// ES2018
/foo.bar/s.test("foo\nbar"); // true
/foo.bar/s.dotAll; // true
複製代碼
// exec() 返回數組的第一項是匹配成功的文本,從第二項起,每項都對應「捕獲括號」裏匹配成功的文本
let regex = /(\d{4})-(\d{2})-(\d{2})/;
regex.exec("1999-12-31"); // ["1999-12-31", "1999", "12", "31", index: 0,groups: undefined]
複製代碼
每一組的匹配含義不容易看出來,並且只能用數字序號引用 \n
容許爲每個組匹配指定一個名字,既便於閱讀代碼,又便於引用。
語法: /?<組名字>(x)/
let regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
regex.exec("1999-12-31");
// ["1999-12-31", "1999", "12", "31", index: 0,groups: {day: "31",month: "12",year: "1999"}]
複製代碼
將匹配結果返回的數組直接解構
let {
groups: { one, two },
} = /^(?<one>.*):(?<two>.*)$/u.exec("foo:bar");
one; // foo
two; // bar
複製代碼
替換時,用 %<組名字>
引用具名組
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
"2015-01-02".replace(re, "$<day>/$<month>/$<year>");
// '02/01/2015'
複製代碼
在 matchAll 出現以前,經過在循環中調用 regexp.exec() 來獲取全部匹配項信息 若是使用 matchAll ,就能夠沒必要使用 while 循環加 exec 方式了
let regexp = /t(e)(st(\d?))/g;
let str = "test1test2";
// match 方式匹配
str.match(regexp); // ['test1', 'test2']
// exec 方式匹配
regexp.exec(str); // ["test1", "e", "st1", "1", index: 0 ]
// matchAll 方式匹配,能夠更好地獲取捕獲組
[...str.matchAll(regexp)]; // [Array(4), Array(4)]
複製代碼
當第一個參數爲正則表達式,第二個參數爲函數時:
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
"2015-01-02".replace(
re,
( matched, // 匹配結果 capture1, // 匹配組1(必須對應上) capture2, // 匹配組2 capture3, // 匹配組3 index, // index input, // input groups // 具名組 ) => {
console.log(matched, capture1, capture2, capture3, index, input, groups);
let { day, month, year } = groups;
return `${day}/${month}/${year}`;
}
); // "02/01/2015"
複製代碼