讲解
正则是锋利的刀,用对地方事半功倍,用错地方后患无穷。第一条原则:知道什么时候不用正则。解析嵌套结构(HTML、XML、JSON、带括号的表达式)超出正则的能力边界——正则不记账,无法保证标签配对。HTML 用 DOM 解析器,JSON 用 JSON.parse,简单的「以什么开头/结尾/包含」用 startsWith、includes 往往比正则更清晰。
第二条原则:警惕灾难性回溯。嵌套量词 (a+)+、重叠分支 (a|aa)+ 在遇到「差一个字符就能匹配」的输入时,回溯路径会指数级增长——/(a+)+$/.test 处理 30 个 a 加一个 X 需要尝试超过十亿条路径,浏览器直接卡死。这就是 ReDoS(正则拒绝服务)攻击的原理。防御方法:不让内层和外层量词匹配同样的字符,(a+)+ 改写成 a+;让相邻路径互斥,用排除型字符类收窄可能性;对线上服务,用带超时的正则引擎或限制输入长度。
第三条原则:把正则当代码维护。复杂模式拆段命名、逐段拼接,可读性远胜一行天书;命名分组自我说明;用例表(正例反例都要有)跟着模式一起进测试。最后一点性能常识:热点路径上预编译模式(JavaScript 里把字面量提出循环,Python 里 re.compile),锚点能加就加,.* 能换具体字符类就换——这些习惯在数据量上来之后都会回报你。
示例
灾难性回溯的反面教材与安全改写:
// 反面教材的结构:内层 a+ 和外层 + 匹配同一批字符,路径重叠
// /^(a+)+$/ 遇到 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaX' 会指数级回溯卡死——不要运行它!
const bad = /^(a+)+$/;
console.assert(bad.test('aaaa')); // 短输入一切正常,问题只在特定输入上爆发
// 安全改写:本例中 (a+)+ 匹配的内容和 a+ 完全等价
const good = /^a+$/;
console.assert(good.test('aaaa'));
console.assert(good.test('aaab') === false); // 瞬间返回 false,不会回溯爆炸
可读性优先:拆段、命名、注释。
// 天书写法(能跑,但三个月后没人敢动):
// const dateRe = /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
// 维护友好的写法:每段有名字,拼起来一目了然
const year = String.raw`\d{4}`;
const month = String.raw`(0[1-9]|1[0-2])`;
const day = String.raw`(0[1-9]|[12]\d|3[01])`;
const dateRe = new RegExp('^' + year + '-' + month + '-' + day + '$');
console.assert(dateRe.test('2026-08-09'));
console.assert(dateRe.test('2026-13-40') === false);
console.assert(dateRe.test('2026-8-9') === false);
// 命名分组让提取代码自我说明
const logRe = /\[(?<time>[^\]]+)\] (?<level>\w+): (?<msg>.*)/;
const m = '[12:00:01] INFO: 启动完成'.match(logRe);
console.assert(m.groups.time === '12:00:01');
console.assert(m.groups.level === 'INFO');
console.assert(m.groups.msg === '启动完成');
杀鸡不用牛刀:简单场景用字符串方法。
// 这些场景正则不是最优解
const filename = 'photo.png';
console.assert(filename.endsWith('.png')); // 比 /\.png$/ 更直白
console.assert('hello world'.startsWith('hello')); // 比 /^hello/ 更直白
console.assert('a,b,c'.includes('b')); // 比 /b/ 更直白
// 但「按模式」判断时正则不可替代
console.assert(/^\d{4}-\d{2}-\d{2}$/.test('2026-08-09')); // 字符串方法做不到这个
常见坑
- 用正则解析 HTML/JSON:嵌套结构不是正则能可靠处理的,历史上无数血泪。DOM 用解析器,JSON 用 JSON.parse。
- 嵌套量词上线:(a+)+、(\w+\s?)+ 这类结构面对恶意输入就是 ReDoS 漏洞。写完正则自问一句:有没有两条路径能匹配同一段文本?
- 只测开心路径:格式错误、超长输入、空串、Unicode 边界都要进用例表。
- 把可读性献祭给简洁:正则的简洁是有代价的。能让三个月后的自己看懂,比少打二十个字符重要得多。
小结
知道何时不用正则,防住灾难性回溯,把模式当代码维护(拆段、命名、测试)。至此你已经掌握了正则的核心概念与工程用法——剩下的就是在真实项目里多用多练。