字符匹配与元字符:点号、字符集、预定义字符类

正则表达式的核心能力,说白了就是「模式匹配」。你写一个模式,它去文本里找符合的片段。元字符就是这些模式的「积木块」。本章我们专门讲三个最基础的:点号、字符集、预定义字符类。掌握它们,你就能写出大部分日常需要的正则了。

点号(.):匹配任意单个字符

点号是最简单的元字符。它匹配除了换行符以外的任意单个字符。我早期刚学正则时,总觉得它就是个通配符——很像 shell 里的 ?,但它只占一个位置。

举个例子:

模式:h.t
匹配:hat、hot、hit、h2t、h%t
不匹配:heat(字符数不对)、h\nt(换行符)

我在处理日志文件时,经常用它来匹配「日期分隔符不统一」的情况。比如 2024-01-01 和 2024/01/01,用 2024.01.01 就能一把抓。当然要注意,点号不会匹配换行。如果你需要匹配包括换行在内的任意字符,那就用 (.|\n) 或者 [\s\S](后面会讲到)。

避坑指南: 我曾经过度依赖点号,写了一个 .+ 去匹配整行,结果因为贪婪匹配把不该吞的内容也吞了。后来才意识到,能用字符集精确匹配的地方,尽量不要用点号。

字符集([]):指定一组候选字符

字符集用方括号包裹。它匹配括号内出现的任意一个字符。说白了,就是「或」的关系,但写法干净很多。

[abc] 匹配 a、b、c 中的任意一个
[0-9] 匹配 0 到 9 之间的任意数字
[a-zA-Z] 匹配所有大小写字母

连字符 - 在字符集里表示「范围」。你想想看,写 [0-9] 比写 [0123456789] 优雅多了。不过要注意,- 放在开头或结尾时,它就只是一个普通连字符,不会产生范围。

我个人习惯将字符集用于「固定长度的可选模式」。比如校验手机号前三位:1[3-9] 就可以匹配 13、14、15…19 开头。这个比写 1(3|4|5|6|7|8|9) 更直观。

取反技巧: 在字符集开头加一个 ^,比如 [^0-9] 匹配任意非数字字符。注意:这个 ^ 只能在字符集内部生效,放在外部就是匹配行首了,别搞混。

预定义字符类:快捷方式

为了少打字,正则里给你备好了几个常用字符类的缩写。我刚开始用的时候记错了不少次,后来画了张表贴在屏幕上才搞定。

预定义类 等价于 说明
\d [0-9] 匹配任意数字(digit)
\D [^0-9] 匹配任意非数字
\w [a-zA-Z0-9_] 匹配字母、数字、下划线(word character)
\W [^a-zA-Z0-9_] 匹配非单词字符
\s [ \t\n\r\f\v] 匹配空白字符(space、制表符、换行等)
\S [^ \t\n\r\f\v] 匹配非空白字符

为什么要单列预定义类?因为效率。我记得有一次在 10GB 的日志里做匹配,用 \d 比 [0-9] 快了一点点——引擎内部有优化。而且写出来更简洁。比如提取 IP 地址:

\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}

看着不舒服?其实 \d{1,3} 指的是「匹配 1 到 3 个数字」,这个我们后面章节会细讲。现在你只要能理解 \d 的含义就行。

核心记忆点: 大写字母预定义类(\D、\W、\S)永远是小写上对应的「取反」。记住这个规律,你就不用死记硬背了。

三者对比:什么时候用谁?

很多初学者会问:点号、字符集、预定义类,到底选哪个?嗯,我一般按这个优先级决定:

  • 优先用预定义类:比如 \d 或 \w,写起来最快,语义明确。
  • 其次用字符集:当预定义类不能精确描述时,比如只要大写字母 [A-Z],或者只要某些特定符号 [+*%]。
  • 最后才用点号:只有当你真的不在乎具体是什么字符时才用点号。说白了,点号是「懒人救星」,但容易误伤。

我曾经在写一个数据清洗脚本时,遇到一行格式很乱,用 .+? 想偷懒,结果把相邻字段给吞了。最后老老实实改用 [^,]+ 按逗号分割,才解决。所以结论是:能精确就别模糊。

知识体系流程图

字符匹配元字符关系图 点号 . 匹配除换行外的任意字符 字符集 [...] 精确匹配一组字符 预定义类 \d、\w、\s 等快捷方式 更精确 更快捷 使用场景建议 • 当你不在乎具体是什么字符时 → 用点号(但要谨慎贪婪匹配) • 当你明确知道可能的字符范围时 → 用字符集 • 当你需要数字/字母/空白等常见类别时 → 用预定义类(最推荐) 示例一句话: 模式:\d+\.\d+ → 匹配 3.14、100.5 等带小数点的数字 模式:[A-Za-z]+ → 匹配纯字母单词(如 hello、World)

避坑与实战技巧

刚用正则时,我踩过这些坑,今天分享给你:

  • 转义元字符:如果你想匹配真正的点号 .,必须写成 \.。否则点号会被当成元字符。同理,[、]、\ 都需要转义。我记得刚开始写匹配文件名的正则,一直匹配不上,查了半天才发现 .png 里的点没转义。
  • 字符集内的特殊字符:在 [] 内部,^ 只在开头取反,其他地方是普通字符;- 表示范围,最好放在开头或结尾避免歧义;\ 仍然需要转义。
  • 预定义类的边界:\w 包含下划线,如果你只想要字母数字,就用 [a-zA-Z0-9]。曾经有同事用 \w+ 匹配用户名,结果包含了下划线导致校验出了问题。
  • 点号和换行:大多数正则引擎中,点号默认不匹配换行。如果你需要匹配跨行内容,要么开启 s 修饰符(DotAll),要么用 [\s\S] 来替代点号。
我的个人小习惯: 在写正则之前,先用一个思维导图想清楚「我要精确匹配哪些字符?哪些是可变的?」然后从预定义类往下选,尽量少用点号。写完一定要用测试工具跑几个边界用例——尤其是空字符串和特殊符号。