1. 正则表达式是国际标准,跨越语言;

  2. 正则表达式是一个规则,用于验证字符串;

使用场景

  1. 数据验证:可以测试输入字符串,以查看字符串内是否出现电话号码模式或信用卡号码模式;

  2. 替换文本:可以使用正则表达式来识别文档中的特定文本,完全删除该文本或者用其他文本替换它;

  3. 从字符串中提取子字符串:可以查找文档内或输入域内特定的文本;

介绍

量词元字符

字符 描述
* 0 到多次,* 是贪婪的,会尽可能多的匹配文字,在它后面加上一个 ? 就可以实现非贪婪或最小匹配
+ 1 到多次,+ 是贪婪的,会尽可能多的匹配文字,在它后面加上一个 ? 就可以实现非贪婪或最小匹配
? 0 或 1 次
{ n } 出现 n 次 (n 为 0 或正整数)
{ n, } 出现 n 到多次 (n 为 0 或正整数)
{ n, m } 出现 n-m 次 (n 为 0 或正整数)

字符类

字符 描述
[ ] 匹配括号内的任意一个字符
/[abc]/g 匹配所有的字符 “a”、“b”、“c”, /[0-9]/g 匹配所有 0-9 中的数字
[^ ] 匹配除了括号内的字符以外的任意一个字符
/[^abc]/g 匹配除了字符 “a”、“b”、“c” 以外的所有任意字符

匹配特定类型的字符

字符 描述
\d 匹配一个数字字符,等价于 /[0-9]/
\D 匹配一个非数字字符,等价于 /[^0-9]/
\w 匹配字母、数字、下划线,等价于 /[A-Za-z0-9_]/
\W 匹配非字母、数字、下划线,等价于 /[^A-Za-z0-9_]/

特殊字符

字符 描述
\ 转义字符,用于匹配特殊字符本身
. 匹配除换行符 (\n、\r) 之外的任何单个字符,相等于 /[^\n\r]/
| 用于指定多个模式的选择

非打印字符

字符 描述
\n 匹配一个换行符
\r 匹配一个回车符
\s 匹配任何空白字符,包括空格、制表符、换页符等等
\S 匹配任何非空白字符
\t 匹配一个制表符

修饰符

字符 描述
i 不区分大小写,将匹配设置为不区分大小写,搜索时不区分大小写
m 多行匹配,使边界字符 ^$ 匹配每一行的开头 (\n 之后的位置) 和结尾 (\n 之前的位置),而不是整个字符串的开头和结尾
g 全局匹配,查找所有的匹配项

边界匹配

字符 描述
^ 匹配字符串的开头 (精确匹配)
$ 匹配字符串的结尾 (精确匹配)
\b 匹配单词边界,它匹配一个 单词的开始单词的结束 的位置,而不匹配任何实际的字符
\bword\b 匹配整个单词 “word”,但不匹配 “words” 或 “sword”
\B 匹配非单词边界,即匹配 单词的内部非单词的开头或结尾
\Bword\B 匹配 “swords” 中的 “word”,但不匹配 “word”、“sword”、 “words”

分组

  1. 分组使用圆括号 () 来创建,主要作用是:

    1. 将多个字符视为一个整体进行操作;
    2. 实现子表达式的逻辑组合;
    3. 为捕获和反向引用奠定基础;
  2. 基本用法示例:

    console.log(/(a|b)c/.exec('abc')); // 匹配"ac"或"bc"
    // 输出 ['bc', 'b', index: 1, input: 'abc', groups: undefined]
    
  3. 非捕获分组:有时只需要分组功能而不需要捕获内容,可以使用 (?:) 语法

    console.log(/(?:a|b)c/.exec('abc'));  // 同样匹配"ac"或"bc",但不会捕获分组内容
    // 输出 ['bc', index: 1, input: 'abc', groups: undefined]
    

捕获

  1. 当使用普通圆括号 () 时,正则引擎会自动 “捕获” 该分组匹配的内容,可以通过编号或名称来引用这些捕获的内容;

  2. 编号捕获:捕获的分组按左括号出现的顺序编号,从 1 开始 (0 表示整个匹配)

    console.log(/(\d{4})-(\d{2})-(\d{2})/.exec('2025-12-23')); // 匹配日期格式,有3个捕获组
    // [
    //    '2025-12-23', 
    //    '2025',       组 1:年(\d {4} 匹配的内容)
    //    '12',         组 2:月(\d {2} 匹配的内容)
    //    '23',         组 3:日(\d {2} 匹配的内容)
    //    index: 0, 
    //    input: '2025-12-23', 
    //    groups: undefined
    // ]
    
  3. 命名捕获:可以为分组指定名称,使引用更清晰;

    console.log(/(?<year>\d{4})-(?<month>\d{1,2})-(?<day>\d{1,2})/g.exec('2025-12-23'));
    // [
    //    '2025-12-23', 
    //    '2025',       组 1:年(\d {4} 匹配的内容)
    //    '12',         组 2:月(\d {2} 匹配的内容)
    //    '23',         组 3:日(\d {2} 匹配的内容)
    //    index: 0, 
    //    input: '2025-12-23', 
    //    groups: {year: '2025', month: '12', day: '23'}
    // ]
    
  4. 反向引用:在正则表达式内部,可以用 \n (其中 n 是一个数字,代表第 n 个捕获组) 引用之前捕获的内容,常用于匹配重复模式;

    JavaScript
    JavaScript
    JavaScript
    JavaScript
    // 1. 匹配重复单词 
    console.log(/(\w+)\s+\1/.exec('hello hello')); 
    
    // ['hello hello', 'hello', index: 0, input: 'hello hello', groups: undefined]
    
    // 2. 匹配 HTML 标签对
    console.log(/<(\w+)>.*?<\/\1>/.exec('<p>content</p>'));
    
    // <(\w+)> 匹配开始标签并捕获标签名
    // .*? 匹配标签内容(非贪婪模式)
    // </\1> 匹配对应的结束标签,\1引用前面捕获的标签名
    // ['<p>content</p>', 'p', index: 0, input: '<p>content</p>', groups: undefined]
    
    // 3. 匹配对称的引号
    console.log(/(["'])(.*?)\1/.exec(`"hello world"`));
    
    // ['"hello world"', '"', 'hello world', index: 0, input: '"hello world"', groups: undefined]
    
    // 4. 找出该字符串中连续的字符
    var s = "aaaabbbbdefgg";
    var reg = /(\w)\1+/g;
    
    while (result = reg.exec(s)) {
        console.log(result);
    }
    
    // [ 'aaaa', 'a', index: 0, input: 'aaaabbbbdefgg', groups: undefined ]
    // [ 'bbbb', 'b', index: 4, input: 'aaaabbbbdefgg', groups: undefined ]
    // [ 'gg', 'g', index: 11, input: 'aaaabbbbdefgg', groups: undefined ]
    

正向预查和负向预查

  1. 预查是一种 零宽度 断言,它不会消耗字符串中的字符,仅用于判断当前位置的前后是否满足特定条件;预查的核心价值是 “筛选位置” 而非 “匹配内容”,这使得它在复杂文本提取、格式验证等场景中极为灵活;

  2. “零宽度” 的含义:正则匹配时,会有一个 “匹配指针” 在字符串上移动,常规元字符 (如\d、a-z) 匹配内容后,指针会向后移动对应长度 (例如匹配 “123” 中的 \d,指针会从位置 0 移到 1) ;而预查仅判断 “当前指针位置是否符合条件”,判断后指针不移动 (宽度为 0)

  3. 四大类型预查对比:

    预查类型 语法格式 核心逻辑 (当前位置) 应用场景举例
    肯定正向预查 A(?=B) 后面是 B,匹配 A 提取带特定后缀的内容 (如.png前的文件名)
    否定正向预查 A(?!B) 后面不是 B,匹配 A 排除特定后缀的内容 (如.tmp文件)
    肯定负向预查 (?<=A)B 前面是 A,匹配 B 提取带特定前缀的内容 (如ID: 后的数字)
    否定负向预查 (?<!A)B 前面不是 A,匹配 B 排除带特定前缀的内容 (如http://开头的 URL)

肯定正向预查

  1. 判断当前位置后面的字符必须符合指定规则,符合则当前位置有效,否则无效;

  2. 语法:要匹配的内容(?=预查条件) “匹配 A,且 A 的后面是 B”(A 是要匹配的内容,B 是预查条件)

  3. 案例:

    JavaScript
    JavaScript
    // 1. 提取带特定后缀的内容(从文件名列表中提取 “以 .png、.csv 结尾的文件名,不含后缀)
    console.log("logo.png, data.csv, bg.png, report.pdf".match(/\w+(?=\.(png|csv))/g)); // ['logo', 'data', 'bg']
    
    // 2. 验证密码是否包含数字
    console.log(/^(?=.*\d).{6,12}$/.test('123456')); // true
    
    // 解析:
    // ^:字符串开头
    // (?=.*\d):判断 “从开头到结尾的任意位置是否有数字”(.*表示任意字符任意次,\d表示数字)
    // .{6,12}:匹配 6-12 个任意字符(密码主体)
    // $:字符串结尾
    

否定正向预查

  1. 判断当前位置后面的字符必须不符合指定规则,不符合则当前位置有效,否则无效;

  2. 语法:要匹配的内容(?!预查条件) “匹配 A,且 A 的后面不是 B”(A 是要匹配的内容,B 是预查条件)

  3. 案例:

    JavaScript
    JavaScript
    // 1. 排除特定后缀的文件(从文件列表中排除 “.tmp临时文件”,提取其他文件)
    console.log("note.txt, temp.tmp, data.xlsx, log.tmp".match(/\w+\.(?!tmp\b)\w+/g)); // ['note.txt', 'data.xlsx']
    
    // 2. 禁止敏感词出现在文本开头(判断用户名是否 “不以 admin 开头”)
    console.log(/^(?!admin).+$/.test('admin123')); // false
    

肯定负向预查

  1. 判断当前位置前面的字符必须符合指定规则,符合则当前位置有效,否则无效;

  2. 语法:(?<=预查条件)要匹配的内容 “匹配 B,且 B 的前面是 A”

  3. 案例:

    JavaScript
    JavaScript
    // 1. 提取 “前缀固定” 的内容(从日志中提取 “ID: 后面的用户 ID”)
    console.log("Login: user1, ID: 1001; Login: user2, ID: 1002".match(/(?<=ID: )\d+/g)); // ['1001', '1002']
    
    console.log("Login: user1, ID: 1001; Login: user2, ID: 1002".match(/\w+: \b(?<=ID: )\d+/g)); // ['ID: 1001', 'ID: 1002']
    
    // 2. 格式化数字(给整数部分加千分位)
    console.log("987654321.123".replace(/(?<=\d)(?=(\d{3})+\.)/g, ',')); // '987,654,321.123'
    
    // 解析:
    // (?<=\d):肯定负向预查,确保当前位置前面是数字(避免在开头加逗号)
    // (?=(\d{3})+\.):肯定正向预查,确保当前位置后面是 “若干个 3 位数字 + 小数点”(即从当前位置到小数点,数字位数是 3 的倍数)
    

否定负向预查

  1. 判断当前位置前面的字符必须不符合指定规则,不符合则当前位置有效,否则无效;

  2. 语法:(?<!预查条件)要匹配的内容 “匹配 B,且 B 的前面不是 A”

  3. 案例:

    JavaScript
    JavaScript
    // 1. 排除 “前缀敏感” 的内容(从文本中提取 “不是http://开头” 的 URL)
    console.log("http://baidu.com, https://google.com, ftp://ftp.server.com".match(/(?<!http:)\/\/.+/g)); 
    
    // ['//google.com, ftp://ftp.server.com']
    
    // 2. 避免重复替换(给文本中的 “book” 加引号,但 “已经有引号的"book"” 不重复加)
    console.log(`I have a book, and a "book".`.replace(/(?<!")book(?!")/g, '"book"')); // 'I have a "book", and a "book".'
    

在线工具

  1. 正则表达式在线测试

  2. 正则表达式可视化图解

打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

你好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 使用场景
  2. 2. 介绍
    1. 2.1. 量词元字符
    2. 2.2. 字符类
    3. 2.3. 匹配特定类型的字符
    4. 2.4. 特殊字符
    5. 2.5. 非打印字符
    6. 2.6. 修饰符
    7. 2.7. 边界匹配
    8. 2.8. 分组
    9. 2.9. 捕获
    10. 2.10. 正向预查和负向预查
      1. 2.10.1. 肯定正向预查
      2. 2.10.2. 否定正向预查
      3. 2.10.3. 肯定负向预查
      4. 2.10.4. 否定负向预查
  3. 3. 在线工具