正则表达式基础:元字符、字符类、量词、分组与捕获

好,咱们直接进入正题。正则表达式这东西,说白了就是一套描述文本模式的"密码"。你给计算机一套规则,它就能在成堆的数据里帮你找到想要的内容。我在做敏感数据识别的时候,正则就是第一道筛子——先把可能的手机号、身份证号这些候选人捞出来。

这一章,咱们把这套密码的四个核心零件拆开讲清楚。掌握了它们,你就能看懂大多数正则写法了。

本章思维导图

下面这张SVG图把四个核心概念串起来了。我建议你把它作为一个整体来看——每个部分都在实际项目中反复出现。

正则表达式核心概念结构图 正则表达式基础 元字符 字符类 量词 分组与捕获 . ^ $ * + ? { } [ ] \ | ( ) [a-z] [^0-9] \d \w \s * + ? {n} {n,} {n,m} (abc) (?:abc) \1 $1 典型应用场景 数据清洗 → 敏感信息提取 → 格式校验 → 日志分析 提示:所有元字符在字符类内部可能失去特殊含义,使用时需注意上下文

1. 元字符:正则的"标点符号"

元字符是什么?就是那些有特殊含义的符号。比如点号 .,它不代表"句号",而是代表"任意单个字符"。我刚开始学的时候,这一点就经常搞混。

元字符 含义 匹配示例
. 匹配除换行符以外的任意单个字符 a.b 可以匹配 acb、a3b
^ 匹配字符串的开头 ^Hello 只匹配开头的 Hello
$ 匹配字符串的结尾 world$ 只匹配结尾的 world
\ 转义符号,将元字符变成普通字符 \. 匹配真正的点号
| 逻辑或 cat|dog 匹配 cat 或 dog
() 分组,稍后细讲 (ab)+ 匹配 ababab

个人经验:在验证电话号码时,我经常用 ^1[3-9]\d{9}$。这里的 ^ 和 $ 用来掐头去尾,确保整串都是号码,而不是某段文字里冒出来的11位数字。这个习惯帮我避免过不少误报。

这里有个关键点要记牢:如果你真的想匹配一个点号、星号这类特殊字符,记得在它前面加反斜杠 \ 转义。比如文件名里常见的 test\.txt。

2. 字符类:给匹配范围画个圈

字符类用方括号 [] 表示,用来指定"我想在这一位上匹配这些字符中的某一个"。这玩意儿在敏感数据识别里太常用了。

// 字符类示例
[abc]       // 匹配 a、b、c 中的任意一个
[a-z]       // 匹配任意小写字母
[0-9]       // 匹配任意数字
[a-zA-Z0-9] // 匹配任意字母或数字
[^0-9]      // 匹配除数字以外的任意字符(^放在开头表示取反)

你看看,身份证号里最后一位可能是数字也可能是X,那字符类就能写成 [0-9Xx]。我在写车牌号识别的时候也用了类似思路——省事又准确。

预定义字符类 等价写法 说明
\d [0-9] 任意数字
\D [^0-9] 任意非数字
\w [a-zA-Z0-9_] 单词字符(字母、数字、下划线)
\W [^a-zA-Z0-9_] 非单词字符
\s [ \t\n\r\f] 空白字符
\S [^ \t\n\r\f] 非空白字符

注意坑位:字符类里面的 ^ 如果放在开头,表示"取反";如果放在中间或结尾,就是普通字符。我见过不少人把这个搞混,结果取反变成了匹配尖括号——哭笑不得。

举个例子:[^abc] 匹配不是 a、b、c 的字符;[a^bc] 匹配 a、^、b、c 这四个字符中的任意一个。

3. 量词:控制重复次数

量词决定前面的表达式要重复多少次。没有量词,正则就成了一对一的匹配——那等于没用。

量词 含义 示例
* 0次或多次 ab*c 匹配 ac、abc、abbc 等
+ 1次或多次 ab+c 匹配 abc、abbc,但不匹配 ac
? 0次或1次 colou?r 匹配 color 和 colour
{n} 恰好n次 \d{4} 匹配恰好4个数字
{n,} 至少n次 \d{2,} 匹配至少2个数字
{n,m} n次到m次 \d{3,5} 匹配3到5个数字

为什么我说这个很重要?你想想看,匹配手机号需要11位数字,如果不用量词约束,那12位的、13位的都可能被误捕。用 \d{11} 就精确了。我记得有一次做金融数据脱敏,就靠这个精准量词把16位信用卡号从大段文本里捞出来的。

避坑指南:默认情况下,* 和 + 是"贪婪"的,它们会尽可能多地匹配。比如用 a.*b 去匹配 aXbYb,它会吞掉整个字符串。如果你想要"非贪婪"模式(尽可能少匹配),可以在量词后面加个 ?,写成 a.*?b。这在提取HTML标签内容时特别有用。

4. 分组与捕获:把匹配结果装进"篮子"

分组用小括号 (),它有两个作用:一是把多个符号当成一个整体来操作,二是把匹配到的内容"捕获"下来,方便后续使用。

// 分组示例
(ab)+           // 匹配 "ab" 的重复,如 "ababab"
(https?://)     // 匹配 "http://" 或 "https://",并把协议部分捕获下来

// 反向引用:用 \1、\2 等引用前面捕获的内容
(\w+)\s+\1     // 匹配重复的单词,如 "hello hello"

反向引用在数据质量检查里很好用。我曾经用它来检测重复的字段值——比如两份数据合并后,同一个身份证号出现了两次,那肯定是脏数据。

分组类型 语法 是否捕获 用途
捕获分组 (pattern) 是 提取数据、反向引用
非捕获分组 (?:pattern) 否 只需要分组,不需要提取内容
零宽断言(前瞻) (?=pattern) 否 匹配某个位置后面跟着特定模式的情况
零宽断言(后顾) (?<=pattern) 否 匹配某个位置前面是特定模式的情况

重点记忆:在替换操作中,捕获组特别有用。比如把 John Doe 换成 Doe, John,可以用 (\w+)\s(\w+) 捕获,然后替换成 $2, $1。这在清洗姓名、地址等敏感字段时能省大量时间。

对了,非捕获分组 (?:...) 的实际应用场景:你需要把一组模式当成整体施加量词,但不需要保留中间匹配结果。比如匹配 (?:https?://)?\w+\.\w+ 来提取URL——前面的协议部分可有可无,捕获它没有意义,用非捕获分组性能更好。


5. 把这些零件组装起来:实战例子

光说不练假把式。咱们用敏感数据识别中最常见的几个模式来练练手。

// 手机号(中国大陆)
^1[3-9]\d{9}$
// 解析:以1开头,第二位3-9,后面9位数字,总共11位

// 身份证号(18位)
^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$
// 解析:包含了地区码、年份、月份、日期、顺序码和校验位

// 邮箱(简化版)
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
// 解析:用户名部分 + @ + 域名部分 + 顶级域名

// IP地址(IPv4,严格版)
^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$
// 解析:每段数字0-255,用点分隔,共4段

我的调试习惯:正则不要一次性写太长。我习惯先用 . 测试基础匹配范围,然后加字符类缩小范围,再加量词控制长度,最后套上 ^$ 锚定边界。写一段、测一段,比最后回来改bug快得多。

你看,这一套组合拳下来,手机、身份证、邮箱、IP地址都能精确定位。在实际数据安全项目里,正则就是第一道防线——把可疑的数据先圈出来,再交给机器学习模型做更细致的分类。

6. 记住几个容易踩的坑

  • 点号不匹配换行:默认情况下,. 不匹配 \n。如果你要跨行匹配,得用 [\s\S] 代替,或者开启 s 模式。
  • 量词默认贪婪:前面说过了,.* 能吞就吞。如果发现匹配结果比预期长,试试加 ? 变成非贪婪。
  • 字符类里的特殊符号:除了 ^(在开头)、-(表示范围)、\(转义)、](结束符)以外,其他元字符在 [] 内部都是普通字符。
  • 分组编号从1开始:正则里的捕获组编号从1开始,$0 或 \0 代表整个匹配内容。

切记:在敏感数据识别场景中,正则表达式本身也可能泄露信息。比如你把公司内部的数据匹配规则写死在代码里并上传到公共仓库,那等于把筛选条件公开了。我一般在项目里把敏感正则单独抽成配置文件,加上访问控制。

好了,正则基础的四个零件就讲完了。你可能会觉得内容不少,但说实话,实际项目中翻来覆去用的就是这些。把它们当成积木块,多拼几次就熟了。