# js正则归纳

89 阅读11分钟

背景

在开发中处理字符串的场景非常常见,而正则作为专业的文本模式匹配工具,可谓是大大的提高了我们处理字符串的能力和效率。但是在业务中使用的时候,因为没有系统的去梳理过正则,简单的还好,稍微复杂一点的,就需要问ai,但ai给出的解法,还需要自己了解和验证,所以还得对不熟悉的点去重新翻阅文档,效率低,自己脑海中也没有对个知识行成较好的记忆。所以为了解决这些痛点,整理归纳一下正则。

说明:各个编程语言的正则规则大致相同,但也会有所差异,所以本文主要围绕javascript中的正则展开.

基本知识

  • pattern(模式):一列普通字符和元字符组成
  • flags(修饰符):用于改变正则表达式匹配行为的特殊指令

创建

字面量:/pattern/flags

  • 编译时机:代码解析阶段
  • 性能:好
const reg = /ab+c/i; // 字面量形式

构造函数:new RegExp(pattern[, flags])

  • 编译时机:运行时编译
  • 性能:差(如每次循坏都要重新编译)
// 首个参数为字符串模式的构造函数
new RegExp("ab+c", "i");
// 需要常规的字符转义规则(在前面加反斜杠\)
const re = new RegExp("\\w+");
const re = /\w+/;

元字符

具有特殊含义的字符,它们不表示字面意义,而是用于控制匹配模式。

基本字符

.(点号)

匹配除\n外的任意单个字符,其实回车符\r也不匹配

  console.log(/a.c/.test('aac')); // true
  console.log(/a.c/.test('a\tc')); // true
  console.log(/a.c/.test('a\rc')); // false
  console.log(/a.c/.test('a\nc')); // false

\(反斜杠)

转义字符,使后面的字符失去特殊含义

console.log(/\./.test('.')); // true
console.log(/\./.test('a')); // false

量词

?(问号)

匹配前面的子表达式零次或一次

console.log(/ab?c/.test('ac')); // true;
console.log(/ab?c/.test('abc')); // true
console.log(/ab?c/.test('abbc')); // false

*(星号)

匹配前面的子表达式零次或多次

console.log(/ab*c/.test('ac')); // true
console.log(/ab*c/.test('abc')); // true
console.log(/ab*c/.test('adc')); // false

+(加号)

匹配前面的子表达式一次或多次

console.log(/ab+c/.test('abc')); // true
console.log(/ab+c/.test('abbc')); // true
console.log(/ab+c/.test('ac')); // false

{}(花括号)

自定义匹配次数
{n}:精确匹配n次
{n,}:至少匹配n次
{n,m}:匹配n-m次

// 精确匹配
console.log(/ab{2}/.test('abb')); // true
console.log(/ab{2}/.test('ab')); // false
// 至少匹配
console.log(/ab{2,}/.test('abb')); // true
console.log(/ab{2,}/.test('abbb')); // true
console.log(/ab{2,}/.test('ab')); // false
// 匹配区间
console.log(/ab{2,3}/.test('abb')); // true
console.log(/ab{2,3}/.test('abbb')); // true
console.log(/ab{2,3}/.test('ab'));  // false
console.log(/ab{2,3}c/.test('abbbbc')); // false

分组、选择字符、字符组

()(圆括号)

定义子表达式或捕获组

console.log(/(ab)+c/.test('abc')); // true
console.log(/(ab)+c/.test('ababc')); // true
console.log(/(ab)+c/.test('bc')); // false

|(竖线)

表示"或"关系, 作用多个字符, 建议用括号括起来

console.log(/abc|adc/.test('abc')); // true
console.log(/abc|adc/.test('adc')); // true
console.log(/abc|adc/.test('abadc')); // false

[](方括号)

定义字符集合,匹配其中任意一个字符

console.log(/[abc]/.test('a')); // true
console.log(/[abc]/.test('b')); // true
console.log(/[abc]/.test('c')); // true

[]内部特殊字符

  • ^(否定字符)

匹配不在方括号中的任意字符

// 匹配除a,b,c外的任意一个字符
console.log(/[^abc]/.test('a')); // false
  • -(连字符)

匹配方括号中范围字符

// 匹配a,b,c中的任意一个字符
console.log(/[a-c]/.test('b')); // true

注: []内除^-外,还有预定义字符组 和 转义字符生效外,其他元字符 都表示字面量

预定义字符组

  • \d:匹配任意数字,等价于[0-9]
  • \D:匹配任意非数字,等价于 [^0-9]
  • \w:匹配任意单词字符(字母、数字、下划线),等价于[a-zA-Z0-9_]
  • \W:匹配任意非单词字符,等价于 [^a-zA-Z0-9_]
  • \s:匹配任意空白字符,等价于[\n\r\t\v\f]
  • \S:匹配任意非空白字符,等价于[^\n\r\t\v\f]

位置(边界)匹配字符

位置:相邻字符之间的位置。

image.png

^(脱字符)

匹配字符串的开始位置

console.log(/^abc/.test('abcd')); // true
console.log(/^abc/.test('abcdefg')); // true
console.log(/^abc/.test('aba')); // false

$(美元符)

匹配字符串的结束位置

console.log(/abc$/.test('dabc')); // true
console.log(/abc$/.test('aabc')); // true
console.log(/abc$/.test('adbc')); // false

\b(单词边界符)

匹配单词边界

  1. ^\w之间的位置
  2. \w\W之间的位置
  3. \w$之间的位置
console.log(/\bcat\b/.test('cat demo')); // true
console.log(/\bcat\b/.test('category')); // false

\B(非单词边界符)

匹配非单词边界

  1. \w\w之间的位置
  2. \W\W之间的位置
  3. ^\W之间的位置
  4. \W$之间的位置
console.log(/\Bcat\B/.test('scategory')); // true
console.log(/\Bcat\B/.test('category')); // false
console.log(/\Bcat\B/.test('cat')); // false
断言(位置条件判断)

?=(先行断言)

检查当前位置之后是否匹配pattern

命名解释

  • 引擎执行方向:正则表达式引擎通常从左到右扫描字符串
  • 当引擎扫描到某个位置时,它需要 "向前看"(Lookahead)来判断当前位置是否满足断言条件,对字符而言,它是相对位置在后面的
const res = 'abcd'.replace(/(?=b)/, '_');
console.log(res); // a_bcd

// 全局模式
const res2 = 'abcd'.replace(/(?=b)/g, '_');
console.log(res2); // a_bcd

匹配机制

  • 位置1(^):后面是a,匹配失败
  • 位置2(a,b):后面是b,匹配成功
  • 位置3(b,c):后面是c,匹配失败
  • 位置4(c,d):后面是d,匹配失败
  • 位置5($):后面为空,匹配失败

?!(负先行断言)

匹配后面不跟着 pattern 的位置。

const res = 'abcd'.replace(/(?!b)/, '_');
console.log(res); // _abcd

// 全局模式
const res = 'abcd'.replace(/(?!b)/g, '_');
console.log(res); // '_ab_c_d_'

匹配机制

  • 位置1(^):后面是a,a!=b,匹配成功
  • 位置2(a,b之间):后面是b,b=b,匹配失败
  • 位置3(b,c之间):后面是c,c!=b,匹配成功
  • 位置4(c,d之间):后面是d,d!=b,匹配成功
  • 位置5($):后面是'',''!=b,匹配成功

?<=(后行断言)

检查当前位置之前是否匹配pattern

命名解释

  • 引擎执行方向:正则表达式引擎通常从左到右扫描字符串
  • 当引擎扫描到某个位置时,它需要 "向后看"(Lookahead)来判断当前位置是否满足断言条件
const res = 'abcd'.replace(/(?<=b)/, '_');
console.log(res); // ab_cd

// 全局模式
const res = 'abcd'.replace(/(?<=b)/, '_');
console.log(res); // ab_cd

匹配机制

  • 位置1(^):前面无字符,匹配失败
  • 位置2(a,b):前面是a,匹配失败
  • 位置3(b,c):前面是b,匹配成功
  • 位置4(c,d):前面是c,匹配失败
  • 位置5($):前面是d,匹配失败

?<!(负后行断言)

位置之前不跟着pattern

const res = 'abcd'.replace(/(?<!b)/, '_');
console.log(res); // _abcd

// 全局模式
const res = 'abcd'.replace(/(?<!b)/g, '_');
console.log(res); // _a_bc_d_

匹配机制

  • 位置1(^):前面无字符,匹配成功
  • 位置2(a,b):前面是a,匹配成功
  • 位置3(b,c):前面是b,匹配失败
  • 位置4(c,d):前面是c,匹配成功
  • 位置5($):前面是d,匹配成功

非捕获&具名捕获


?:(非捕获)

仅保留括号的功能,但不存储捕获组的内容,因此能提升正则表达式的效率

const reg = /1(\d)/;
const str = "12134";
console.log(reg.exec(str));
// [ '12', '2', index: 0, input: '12134', groups: undefined ]

// 不存储捕获组内容
const reg = /1(\d)/;
const str = "12134";
console.log(reg.exec(str));
// [ '12', index: 0, input: '12134', groups: undefined ]

?<name>具名捕获

给捕获组进行命名,使用返回捕获结果的正则方法时,存储在groups中

const reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const matchObj = reg.exec("2024-05-15");
if (matchObj) {
  console.log(matchObj.groups); // 输出: { year: '2024', month: '05', day: '15' }
  console.log(matchObj.groups.year); // 输出: '2024'
  console.log(matchObj.groups.month); // 输出: '05'
  console.log(matchObj.groups.day); // 输出: '15'
}

其他元字符

\n(换行符)

匹配换行符(Line Feed)

  • ASCII 码值:10(十进制)或 0x0A(十六进制)
  • 表示文本行的结束,光标移到下一行的开始位置
  • Unix/Linux/MacOS
    • 使用 \nLF)作为换行符
    • 文件中的换行符显示为单个字符

\r(回车符)

匹配回车符(Carriage Return )

  • ASCII 码值:13(十进制)或 0x0D(十六进制)
  • 表示将光标移回到当前行的起始位置,不移动到下一行
  • Windows/DOS:
    • 使用 \r\nCRLF, Carriage Return + Line Feed)的组合作为换行符
    • 文件中的换行符显示为两个连续字符

\t(制表符)

匹配制表符

\v(垂直制表符)

匹配垂直制表符

\f(换页符)

匹配换页符

修饰符

正则表达式修饰符(也称为模式修饰符或标记)是用于改变正则表达式匹配行为的特殊指令

i(ignore case)

忽略大小写

console.log(/abc/i.test('Abc')); // true
console.log(/abc/.test('Abc')); // false

g(global)

全局模式

  1. 如果能自动匹配多次的话,匹配多次(String.matchString.replace)。
  2. 自动变更lastIndex
  3. 查找过程中遇到错误pattern跳过继续查找。
  4. String.matchAll,String.replaceAll必须使用g
// 全局模式
const str = "hello world";
const reg = /l/;
const regByG = /l/g;
console.log(str.replace(reg, "*")); // he*lo world
console.log(str.replace(regByG, "*")); // he**o wor*d

// 自动变更lastIndex
const str = "hello world";
const reg = /l/;
const regByG = /l/g;
str.replace(reg, (match, index, input) => {
  console.log(index);
  return match;
});
// 2
str.replace(regByG, (match, index, input) => {
  console.log(index);
  return match;
});
// 2 3 9

s(single line/dotall)

单行模式

  1. 使点号.匹配包括换行符(\n,\r)在内的所有字符
console.log(/a.b/s.test('a\nb')); // true
console.log(/a.b/.test('a\nb')); // false

m(multiline)

多行模式。
改变 ^$的行为,使其匹配每行的开头和结尾,而不仅是整个字符串的开头和结尾。

const str = `abc\nafc\nadc`;
const reg = /^a/gm;
const newStr = str.replace(reg, "&");
console.log(newStr);
// &bc
// &fc
// &dc

const reg = /^a/g;
const newStr = str.replace(reg, "&");
// &bc
// afc
// adc

y(sticky)

粘性匹配

  1. 必须从正则表达式的 lastIndex 位置开始匹配,如果该位置不满足条件,则匹配失败(正则默认lastIndex = 0)。
  2. 用于严格的连续匹配。
// 全局匹配
const str = "abcabc";
const regexGlobal = /a./g;
console.log(regexGlobal.exec(str));
// [ 'ab', index: 0, input: 'abcabc', groups: undefined ]
console.log(regexGlobal.exec(str));
// [ 'ab', index: 3, input: 'abcabc', groups: undefined ]
console.log(regexGlobal.exec(str)); 
// null

// 粘性匹配
const regexSticky = /a./y;
console.log(regexSticky.exec(str));
// [ 'ab', index: 0, input: 'abcabc', groups: undefined ]
console.log(regexSticky.exec(str)); 
// null(因为第二次从位置2开始,字符是c,不匹配)

// 调整lastIndex继续粘性匹配
regexSticky.lastIndex = 3;
console.log(regexSticky.exec(str));
// [ 'ab', index: 3, input: 'abcabc', groups: undefined ]

粘性匹配y跟全局匹配g的区别

  1. 起始位置
  • g:只要后续位置存在匹配项,就能够继续进行匹配。
  • y:必须从lastIndex开始
  1. lastIndex 的影响
  • 当手动设置 lastIndex 时,g 标志会从这个设定的位置开始尝试匹配。
  • 而 y 标志则严格要求必须从 lastIndex 位置开始匹配,否则不会进行后续查找
  1. 匹配失败后的处理
  • 若 g 标志在 lastIndex 位置匹配失败,它会继续在后续位置寻找匹配项。
  • 要是 y 标志在 lastIndex 位置匹配失败,整个匹配过程就会终止,并且 lastIndex 会被重置为 0

应用场景

  • 全局查找符合条件的片段
  • 严格按顺序解析文本(如词法分析)

贪婪匹配&惰性匹配

正则本身是贪婪的,会尽可能多匹配符合模式的字符

贪婪量词

所有量词默认都是贪婪的

惰性量词

贪婪量词后面加?

  • ??:匹配 0 次或者 1 次,尽可能少匹配。
  • *?:匹配 0 次或多次,尽可能少匹配。
  • +?:匹配 1 次或多次,尽可能少匹配。
  • {n,}:至少匹配 n 次,但尽可能少匹配。
  • {n,m}: 匹配次数在 n 到 m 之间,但尽可能少匹配。
const regex = /\d{2,5}/g;
const str = '123 1234 12345 123456';
// 贪婪匹配
console.log(str.match(regex));
// [ 123, 1234, 12345, 12345 ]

// 惰性匹配
const regex2 = /\d{2,5}?/g;
console.log(str.match(regex));
// [ 12, 12, 34, 12, 34, 12, 34, 56  ]

应用:提取标签内的内容

const html = "<div>Hello</div><div>World</div>";
const regex = /<div>(.*?)<\/div>/g; // 惰性匹配

const matches = [...html.matchAll(regex)];
matches.forEach(match => console.log(match[1]));
// 输出:
// Hello
// World

const regx = /<div>(.*)<\/div>/g; // 贪婪匹配
const matches = [...html.matchAll(regex)];
matches.forEach(match => console.log(match[1]));
// 输出:
// <div>Hello</div><div>World</div>

分组引用

通过括号创建的子表达式,可以通过多种方式进行引用,大大减少表达式的复杂度

替换引用

在正则表达式的替换字符串中引用捕获组的内容,目前只有String.replace中使用。
使用$1$2${n}来引用捕获到的内容,按顺序1-n进行引用,其作用范围仅限于单次正则匹配操作,多个正则之间引用独立。

const string = '2021-08-14';
const reg = /(\d{4})-(\d{2})-(\d{2})/;
// 方法一
const result1 = string.replace(reg, '$2/$3/$1')
console.log(result1); // 输出:  08/14/2021
// 方法二
const result2 = string.replace(reg, (_,$1,$2,$3) => {
    return `${$2}/${$3}/${$1}`;
});
console.log(result2); // 输出:  08/14/2021

具名引用

在正则表达式的替换字符串中引用具名捕获的内容,目前只有String.replace中使用。
使用$<name>来引用具名捕获内容

const str = '2023-05-15';
const newStr = str.replace(
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
'$<month>/$<day>/$<year>'
);
console.log(newStr); // "05/15/2023"

反向引用

在正则表达式内部引用之前捕获的内容。
使用\1\2\n来引用对应捕获组匹配的内容,确保后续匹配的内容和前面某捕获组的内容完全一样。

/*    
    写一个正则支持以下三种格式  
        2016-06-12  
        2016/06/12  
        2016.06-12
*/
const regex = /(\d{4})([-/.])\d{2}\2\d{2}/; // \2匹配表示([-/.])匹配到的内容
const string1 = "2017-06-12";
const string2 = "2017/06/12";
const string3 = "2017.06.12";
const string4 = "2016-06/12";

console.log( regex.test(string1) ); // true
console.log( regex.test(string2) ); // true
console.log( regex.test(string3) ); // true
console.log( regex.test(string4) ); // false
// 解释:([-/.])匹配-,而\2表示引用-,-跟/不匹配,

全局引用

引用全局最近一次正则实例匹配成功的内容。
使用RegExp.$1RegExp.$2RegExp.${n},用于存储最近一次正则匹配成功操作中的捕获组的内容,因此存在多个正则时,其值会以后者为准。

// 第一个正则匹配:捕获两个单词
const regex1 = /(\w+) (\w+)/;
regex1.exec("Hello World");
console.log(RegExp.$1); // 输出: "Hello"(第一个捕获组)
console.log(RegExp.$2); // 输出: "World"(第二个捕获组)
// 第二个正则匹配:捕获两个数字(覆盖之前的全局属性)
const regex2 = /(\d+) (\d+)/;
regex2.exec("123 456");
console.log(RegExp.$1); // 输出: "123"(第二个正则的第一个捕获组,覆盖了之前的"Hello")
console.log(RegExp.$2); // 输出: "456"(第二个正则的第二个捕获组,覆盖了之前的"World")
// 再次执行第一个正则,会重新覆盖
regex1.exec("Foo Bar");
console.log(RegExp.$1); // 输出: "Foo"(重新被第一个正则的捕获组覆盖)

注意事项

  • 不推荐使用全局引用,尤其是复杂的场景下,且该特性在浏览器中非标准化。
  • 反向引用如果不存在,众多资料表示在js中,不存在会报错,但实测没有报错,推荐用try catch包裹,当然也有部分引擎会解析为普通字符或者无效语句。
  • 反向引用的捕获组有量词如何理解?量词只作用捕获组,而捕获组只会记录最后一次捕获的内容,如下代码:
const regex = /^(\d){2}\1c/;
console.log(regex.test("123c")); // false,最后一次捕获到的内容是2
console.log(regex.test("122c")); // true

功能分类

对支持正则的方法的功能进行分类,对这些方法返回的结果进行归纳总结,从而在面对不同问题时使用最恰当的方法,同时增加记忆。

前言

正则匹配跟lastIndex息息相关,我们对lastIndex的认知更多停留在RegExp.lastIndex全局属性,但正则实例也有lastIndex属性

  • gy:匹配的过程中,lastIndex始终为0,不可变更,修改无效,多次调用调用会得到相同的答案。
  • gy修饰符:lastIndex自动变更,也支持手动变更,多次调用也就会得到不同的答案。
  • 值得注意的点是当匹配失败结果为null时,lastIndex会重置为0。

捕获组结构

const regex = /\d(\w)/; // 无 g 修饰符
const str = 'a1b2c3';
const result = regex.exec(str);
console.log(result);
// 输出:
// [
//   '1b',        // 匹配字符串
//   'b',         // 第一个捕获组 (\w)
//   index: 1,    // 匹配起始位置
//   input: 'a1b2c3',
//   groups: undefined
// ]

// 默认捕获组内容结构(无g修饰符)
type TSingleCatchGroup = [
    match: string, // 匹配字符
    catch1: string, // 从1到后面都是捕获组的内容
    catch2: string,
    catch{n}: string
]
// 捕获组内容的方法(虽然是数组,但在js中,数组也是对象,可以给它添加自定义属性)
interface TSingleCatchGroupProp {
    index: 0; // 匹配的起始位置,同lastIndex
    input: string; // 原字符串
    groups?: Record<string, string>; // 命名捕获组内容,详情查看具名捕获组
}

ps:这里记忆的时候,很多初学者会把index、input、groups记在数组里,其实不是。数组只有匹配项及捕获组内容,这三个属性是挂在数组下的。

验证

RegExp.test

  • 检查字符是否符合具体的格式。
  • 返回值类型:boolean
  • g多次调用根据lastIndex位置,匹配失败则重置lastIndex
const regex = /^\d{4}-\d{2}-\d{2}$/; // 验证 YYYY-MM-DD 日期格式
console.log(regex.test('2023-01-01')); // true
console.log(regex.test('2023/01/01')); // false

查找

String.search

  • 类似indexOf,但只支持正则。
  • 返回值类型:number,索引位置,从0开始,未找到返回-1
  • g:忽略g,只返回首个匹配位置。
const str = "hello world";
const reg = /world/;
console.log(str.search(reg)); // 6

提取

RegExp.exec

运用场景:复杂场景、需要捕获组信息的时候

  • 提取匹配项、捕获组内容。
  • 返回值类型:TSingleCatchGroup
  • g多次调用根据lastIndex得到不同的TSingleCatchGroup
/** ---------- 不带g -------**/
const regex = /\w(\d)/;
const str = "a1b23c";

console.log(regex.exec(str)); 
// 输出:[ 'a1', '1', index: 0, input: 'a1b23c', groups: undefined ]
console.log(regex.exec(str));
// 输出:[ 'a1', '1', index: 0, input: 'a1b23c', groups: undefined ]


/** ---------- 带g -------**/
const regex = /\w(\d)/g;
const str = "a1b23c";
console.log(regex.exec(str)); 
// 输出:[ 'a1', '1', index: 0, input: 'a1b23c', groups: undefined ]
console.log(regex.exec(str));
// 输出:[ 'b2', '2', index: 2, input: 'a1b23c', groups: undefined ]

String.match

运用场景:简单场景、不需要捕获组信息,仅需匹配项

  • 提取捕获项、捕获组内容。
  • 返回值类型:TSingleCatchGroup
  • g:直接得到所有的匹配内容string[](匹配项数组,无捕获组信息)
/** ---------- 不带g -------**/
const str = "2023-05-15";
const match = str.match(/(\d{4})-(\d{2})-(\d{2})/);
console.log(match);
// 输出:[
//  '2023-05-15',
//  '2023',
//  '05',
//  '15',
//  index: 0,
//  input: '2023-05-15',
//  groups: undefined
// ]


/** ---------- 带g -------**/
const str = 'Dates: 2023-05-15, 2024-06-16';
const matches = str.match(/(\d{4})-(\d{2})-(\d{2})/g);
console.log(matches); 
// 输出:["2023-05-15", "2024-06-16"](只有匹配项,无捕获组信息)

String.matchAll

运用场景:复杂场景,且需要捕获组信息的时候

  • regexp 必须是一个带 g 修饰符的正则表达式(否则会抛出错误)
  • 返回值类型:迭代器对象,包含所有匹配结果TSingleCatchGrou[]
const str = 'Dates: 2023-01-01, 2024-05-15';
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/g;
// 使用 for...of 遍历迭代器
for (const match of str.matchAll(regex)) {
    console.log(`
        匹配位置: ${match.index}
        完整日期: ${match[0]}
        年: ${match.groups.year}
        月: ${match.groups.month}
        日: ${match.groups.day}
  `);}
  // 输出:
  // 匹配位置: 7
  // 完整日期: 2023-01-01
  // 年: 2023
  // 月: 01
  // 日: 01
  
  // 匹配位置: 20
  // 完整日期: 2024-05-15
  // 年: 2024
  // 月: 05
  // 日: 15

结果转数组

// 使用Array.from或者[...]等转换为数组
const str = 'Dates: 2023-01-01, 2024-05-15';
const matchList = Array.from(str.matchAll(regex));
matchList.forEach((item) => {
  console.log(`
    匹配位置: ${item.index}
    完整日期: ${item[0]}
    年: ${item.groups.year}
    月: ${item.groups.month}
    日: ${item.groups.day}
  `);
});

替换

String.replace

运用场景:需要替换字符串

  • 替换匹配成功的字符串
  • 回调函数参数:[match、catch1、catch2、...、lastIndex、input]
  • 返回值类型:string
  • g:替换所有匹配成功的字符串
  • 支持替换引用: $1$2$n
  • 支持具名引用:$<name>
// 替换引用 
const str = 'John Smith';
const newStr = str.replace(/(\w+) (\w+)/, '$2, $1');
console.log(newStr); // "Smith, John"

// 具名引用 
const str = '2023-05-15';
const newStr = str.replace(
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
'$<month>/$<day>/$<year>'
);
console.log(newStr); // "05/15/2023"

// 回调函数 
一般使用回调的情况比较多,命名自由,灵活方便
const str = 'Today is 2023-05-15';
const newStr = str.replace(
/(\d{4})-(\d{2})-(\d{2})/,
(match, year, month, day) => `${month}/${day}/${year}`
);
console.log(newStr); // "Today is 05/15/2023"

String.replaceAll

运用场景:无需正则表达式的简单替换场景

  • 直接替换所有匹配的字符串,无需正则表达式
  • 如果是正则表达式,则必须带g修饰符,否则报错(推荐使用String.replace)
// 字符串参数(无需正则)
'hello'.replaceAll('l', 'x'); // "hexxo"
// 正则参数(必须带 g)
'hello'.replaceAll(/l/g, 'x'); // "hexxo"
// 错误:正则不带 g
'hello'.replaceAll(/l/, 'x'); // TypeError

总结

  1. 创建方法非动态创建优先使用字面量
  2. 字符跟元字符改变pattern,修饰符改变行为
  3. gy的相同点在于都会变更lastIndex,不同点在于y必须连续正确匹配,而g可以跳过。对于全局匹配,只有g可以,y必须多次调用。
  4. 横向匹配:?+*{n,m}(量词)
  5. 纵向匹配:[](字符组)、|(选择字符)
  6. 惰性匹配:?(量词后面追加)
  7. ()括号内部的元字符
    • 断言(位置):?=?!?<=?<!
    • 非捕获:?:
    • 具名捕获:?<name>
    • 惰性匹配:?
    • 反向引用:\1\2
  8. 特殊引用
    1. 正则内部:\1\2(反向引用)
    2. String.replace$1$2(替换引用)、$<name>(具名引用)
  9. 牢记方法
    1. 验证:RegExp.test
    2. 查找位置:String.search
    3. 提取内容
      1. 只需匹配项:String.match
      2. 需要捕获信息:RegExp.execString.matchAll
    4. 替换内容
      1. 无正则:String.replaceAll
      2. 需要正则:String.replace

最后

  1. 有错误欢迎指正,谢谢大家。
  2. 运用篇可以看这位大佬写的juejin.cn/post/702167…