- 约1357字
- 技术
- 2026年4月14日
很多人觉得正则表达式"够用就行",但我见过太多因为一个正则写错,导致整个功能失效的案例。上周同事的一个正则匹配线上数据,全部返回空,排查了半天才发现是量词写错了。今天把这5个最常见的坑整理出来,看完你绝对会回来点赞。
1. 量词写错:* 和 + 的区别
最常见的错误之一是把 *(零或多)和 +(一或多)搞混。写过这样的代码吗?
const pattern = /https:*\.\w+/
表面上看起来是匹配 “https” 后面跟任意字符再加域名。但 * 表示"零个或多个",也就是说"https"后面不跟任何字符也能匹配成功。这就会导致 httpcom 这样的字符串也被匹配到。
正确写法应该是:
const pattern = /https:+\.\w+/ // 加号表示至少一个字符
判断方法:如果你不确定需要匹配至少一个还是允许零个,先想清楚业务场景。URL中冒号后面必须有字符,所以用 +;文件名允许空后缀时,才用 *。
2. 贪婪与惰性:.* 和 .*?
正则默认是贪婪匹配,会尽可能多地匹配字符。看这个例子:
const html = '<div>标题</div><p>内容</p>'
const match = html.match(/<div>.*<\/div>/)
你期待匹配 <div>标题</div>,但因为 .* 是贪婪的,它会一路吃到最后一个 </div>,结果匹配到整个字符串。
解决办法是使用惰性匹配 .*?:
const match = html.match(/<div>.*?<\/div>/)
// 结果: <div>标题</div>
一个技巧:大部分情况下,如果你发现匹配结果超出预期,首先怀疑贪婪匹配。改成惰性匹配往往能解决问题。
3. 转义遗漏:点号不是真的点号
正则中 . 匹配任意字符,但如果你要匹配字面意义上的点号,必须写成 \.。
// 错误:会匹配任何字符
const email = 'test@example.com'
const valid = /.+@.+/.test(email) // 看似正确,但.会匹配@
// 正确:点号需要转义
const valid = /.+@.+\..+/.test(email)
这个问题在匹配文件扩展名时特别容易犯:
// 错误:.md 会匹配任意字符
const isMd = /\.md$/.test('file')
// 正确
const isMd = /\.md$/.test('file.md')
记住:正则特殊字符包括 . ^ $ * + ? { } [ ] \ | ( ),如果要匹配字面意义,都要加反斜杠转义。
4. 字符类写反:中括号里的^
[^abc] 表示"除了abc以外的任意字符",但很多人会把它和 [abc] 搞混。
// 匹配非数字
const noDigit = /[^0-9]+/.test('abc') // true
// 匹配数字(不要^)
const hasDigit = /[0-9]+/.test('abc') // false
曾经有个同事用 [^a-zA-Z] 过滤用户名,本意是移除非字母字符,结果把所有字母都过滤掉了,因为他放在了过滤逻辑的反面。
一个区分方法:方括号内的 ^ 是"非",方括号外才是"开头"。
5. 边界匹配:$ 和 \Z 的区别
$ 匹配行尾,但要注意它匹配的是行尾而不是字符串结尾。看这个例子:
const text = 'line1\nline2'
const match = /\w+$/.test(text) // true,因为匹配到 line2
如果要确保整个字符串符合模式,用 \Z 更安全。另外,多行模式下 $ 会匹配换行符前的位置,如果不确定行为,用 (?=$) 或 (?!\w) 做精确边界。
// 确保是完整的单词
const wordBoundary = /\bword\b/
边界问题最隐蔽:测试数据没问题,上线就挂,因为真实数据的换行符、空格位置和测试数据不一样。
总结
正则表达式看起来简单,但坑特别多。这5个错误覆盖了我日常排查问题时的80%场景:
- 分清
*和+,明确要"零个"还是"至少一个" - 优先用惰性匹配
.*?,避免贪婪过度 - 特殊字符要转义,别偷懒
[^...]是排除,不是包含- 边界符要看场景,
$和\b适用情况不同
如果你正在写正则,先拿这5条对照检查一遍,能帮你避开90%的坑。有问题评论区见,帮你排查。