# 正则表达式 RegExp

# 正则表达式的规则

# 词元

词元,用来表示字符

元字符 匹配规则
\d 数字 相当于 [0-9]
\D 非数字 相当于 [^\d] [^0-9]
\w 字母数字下划线 相当于[a-zA-Z0-9_]
\W 非字母数字下划线 相当于[^\w] [^a-zA-Z0-9_]
\s 空格字符 相当于[\n\t\r\v\f]
\S 非空格的其他字符 相当于[^\s] [^\n\t\r\v\f]
. 任意,匹配任意的单个字符,回车、换行除外
\ 转义,匹配这些字符需要用转义来表示 \^ \. \[ \$ \( \) \\

# 数量

量词符 匹配规则 等价
{n} 表示恰好重复n
{n,} 示至少重复n
{n,m} 示重复不少于n次,不多于m
* 重复 0 次以上 相当于{0,}
+ 至少一次 相当于{1,}
? 重复 0 次或 1 次 相当于{0, 1}

# 逻辑

关系符 说明 举例
分支结构,表示“或关系”(OR)
- 连接,/[A-Z]/表示匹配 26 个大写字母
[] 集合,字符集,匹配方括号内包含的任意字符 /[xyz]/表示但凡出现 x, y, z 即可匹配
[^] 取反,否定字符集,匹配不包含的任意字符 /[^xyz]/表示除了xyz之外都可以匹配
(xyz) 组合,字符组,按照确切的顺序匹配字符,相当于匹配一个子串

# 边界

位置符 说明 举例
^ ^ 匹配字符串的开始位置
$ $ 匹配字符串的结束位置

# 断言

# 正向先行断言(?=pattern)

代表字符串中的一个位置,紧接该位置之后的字符序列能够匹配 pattern。

"The fat cat sat on the mat."
  .match(/(T|t)he(?=\sfat)/)(
    // ['The', 'T', index: 0, input: 'The fat cat sat on the mat.'
    // 匹配`The`或者`the`,后面必须带有`\sfat`,因此第一个 The 会被匹配
    "happynewyear2022goodluck"
  )
  .match(/.*(?=2022)/);
// ['happynewyear', index: 0, input: 'happynewyear2022', groups: undefined]
// 匹配2022前面的所有内容

# 正向后行断言(?<=pattern)

代表字符串中的一个位置,紧接该位置之前的字符序列能够匹配 pattern。

"The fat cat sat on the mat."
  .match(/(?<=(T|t)he\s)(fat|mat)/)(
    // ['fat', 'T', 'fat', index: 4, input: 'The fat cat sat on the mat.', groups: undefined]
    // 匹配前面带有`The `或者`the `的 fat 或者 mat,因此第一个fat会被匹配
    "happynewyear2022goodluck"
  )
  .match(/(?<=2022).*/);
// ['goodluck', index: 16, input: 'happynewyear2022goodluck', groups: undefined]
// 匹配2022后面的所有内容

# 负向先行断言?!pattern

同理,代表字符串中的一个位置,紧接该位置之后的字符序列不能匹配 pattern。

"The fat cat sat on the mat.".match(/(T|t)he(?!\sfat)/);
// ['the', 't', index: 19, input: 'The fat cat sat on the mat.', groups: undefined]
// 匹配`Th`或者 `the`,后面不能带有`\sfat`,因此第二个 the 会被匹配

# 负向后行断言(?<!pattern)

代表字符串中的一个位置,紧接该位置之前的字符序列不能匹配 pattern。

"The fat cat sat on the mat.".match(/(?<!(T|t)he\s)(cat|mat)/);
// ['cat', undefined, 'cat', index: 8, input: 'The fat cat sat on the mat.', groups: undefined]
// 匹配前面没有`The`或者`the`的 cat 或者mat因此 匹配到了cat

# 标记

标记 描述
i 不区分大小写 ignore
g 全局搜索 global
m 多行匹配
/.at(\.)?$/gm =>  The fat
                  cat sat
                  on the mat.

匹配到了每一行最后的任意字符加at加点或者不加点 fat sat mat.

# 正则表达式实践

# 匹配中文(连接)

2020 的今天,[\u4e00-\u9fa5]还能匹配到所有中文吗? https://juejin.cn/post/6844904116842430471 (opens new window)

let reg1 = /[\u4e00-\u9fa5]/gm; // ×
let reg2 = /[\u4e00-\u9fd5]/gm; // ×
let reg2 = /[\u4e00-\u9fff]/gm; // √
console.log("\u4e00"); // '一' '一'
console.log("\u95a5"); // '龥' '閥'
console.log("\u9fd5"); // '龥' '龥'

结论: 使用 /[\u4e00-\u9fff]/

# 匹配前后空白(开头结尾)

let reg1 = /(^\s*)|(\s*$)/g;

# 匹配常规字符(元字符)

// 只能输入字母数字下划线
let reg1 = /^\w+$/;
// 只能输入数字
let reg2 = /^\d+$/;
// 匹配小写字母
let reg3 = /[A-Z]/;
// 匹配紧跟这小写字母的大写字母
let reg4 = /(?<=[a-z])([A-Z])/g;

# 匹配代码写法(断言)

// 小驼峰转下划线
let reg1 = /(?<=[a-z])([A-Z])/g;
let str1 = "openSettingDialog";
let str2 = str1.replace(reg1, (match) => {
  return "_" + match.toLowerCase();
});
console.log(str2);
// 'open_setting_dialog'
// 同理
// 下划线转小驼峰
let reg2 = /(?<=\w+)(_[a-z])(?=\w+)/g;
let str3 = "open_setting_dialog";
let str4 = str3.replace(reg2, (match) => {
  return match.replace("_", "").toUpperCase();
});
console.log(str4);
// 'openSettingDialog'

# 匹配标签(断言)

// 匹配a标签中的href
let reg1 = /(?<=<a.*href=")(.+)(?=")/g;

# 匹配数字位置

// 将123456789转化为123,456,789
let reg = /(?!^)(?=(\d{3})+$)/g;
let str = "123456789";
let str2 = str.replace(reg, ",");
console.log(str2);
// 将手机号18379836654转化为183-7983-6654
// 匹配位置 转换
let reg = /(?=(\d{4})+$)/g;
let str = "18379836654";
let str2 = str.replace(reg, "-");
console.log(str2);