一、正则表达式核心规则
1. 字符匹配:基础单元
正则的最小单位是“字符匹配规则”,分为普通字符和元字符(有特殊含义的字符)。
- (1) 普通字符
直接匹配自身的字符,如字母、数字、符号(a、5、!、-等)。
- 示例:
abc → 匹配字符串中的“abc”(如“abc123”“xabcx”)。
- (2) 元字符:特殊功能字符
元字符是正则的核心,需重点掌握。常用元字符及含义如下:
| 元字符 | 含义 | 示例 |
|---|
| . | 匹配除换行符(\n)外的任意单个字符 | a.b → 匹配 “aab” “acb” “a1b”,不匹配“a\nb” |
| * | 匹配前面的字符零次或多次(贪婪) | a* → 匹配“” “a” “aa” “aaa” |
| + | 匹配前面的字符一次或多次(贪婪) | a+ → 匹配 “a” “aa” “aaa”,不匹配“” |
| ? | 匹配前面的字符零次或一次;或修饰量词为“非贪婪” | a? → 匹配“”“a”;a.*?b → 非贪婪匹配(见下文) |
| ^ | 匹配行的开头(默认单行模式) | ^abc → 匹配“abc123”,不匹配“123abc” |
| $ | 匹配行的结尾(默认单行模式) | abc$ → 匹配“123abc”,不匹配“abc123” |
| [] | 字符类:匹配括号内的任意一个字符 | [a-z] → 匹配任意小写字母;[0-9] → 任意数字 |
| [^] | 否定字符类:匹配不在括号内的任意一个字符 | [^0-9] → 匹配非数字字符 |
| \ | 转义字符:将元字符转为普通字符(如.匹配“.”) | a\.b → 匹配“a.b”,不匹配“aab” |
| ` | ` | 选择符:匹配“ |
| () | 分组:将多个字符视为一个整体;捕获组(见下文) | (ab)+ → 匹配 “ab” “abab” “ababab” |
- (3) 预定义字符类:简化字符类的缩写
为常用字符类提供的简写,提高正则可读性。
| 预定义类 | 等价于 | 含义 |
|---|
| \d | [0-9] | 任意数字(digit) |
| \D | [^0-9] | 任意非数字 |
| \w | [a-zA-Z0-9_] | 任意字母、数字、下划线(word) |
| \W | [^a-zA-Z0-9_] | 任意非字母、数字、下划线 |
| \s | [ \t\n\r\f] | 任意空白符(空格、制表符、换行等) |
| \S | [^ \t\n\r\f] | 任意非空白符 |
| \b | - | 单词边界(如“hello”中的“h”前、“o”后) |
| \B | - | 非单词边界 |
2. 量词:控制字符重复次数
量词用于指定“前面的字符/分组”的重复次数,分为贪婪量词(默认,尽可能多匹配)和非贪婪量词(在量词后加?,尽可能少匹配)。
| 量词 | 类型 | 含义 | 示例(匹配“aabbaa”) |
|---|
| * | 贪婪 | 零次或多次 | a.*b → 匹配“aabba”(从第一个a到最后一个b) |
| *? | 非贪婪 | 零次或多次(最少) | a.*?b → 匹配“aab”(从第一个a到最近的b) |
| + | 贪婪 | 一次或多次 | a.+b → 匹配“aabba” |
| +? | 非贪婪 | 一次或多次(最少) | a.+?b → 匹配“aab” |
| ? | 贪婪 | 零次或一次 | ab? → 匹配“a”“ab” |
| ?? | 非贪婪 | 零次或一次(最少) | ab?? → 匹配“a” |
| {n} | 贪婪 | 恰好n次 | a{2} → 匹配“aa”,不匹配“a”“aaa” |
| {n,} | 贪婪 | 至少n次 | a{2,} → 匹配“aa”“aaa”“aaaa” |
| {n,m} | 贪婪 | 至少n次,最多m次 | a{2,3} → 匹配“aa”“aaa” |
| {n,}? | 非贪婪 | 至少n次(最少) | a{2,}? → 匹配“aa” |
| {n,m}? | 非贪婪 | 至少n次,最多m次(最少) | a{2,3}? → 匹配“aa” |
3. 位置匹配:匹配“间隙”而非字符
位置匹配不消耗字符,仅匹配字符串中的“间隙”(如行开头、单词边界),常用于精准验证。
| 位置符 | 含义 | 示例 |
|---|
| ^ | 行开头(单行模式:整个字符串开头;多行模式:每行开头) | ^1[3-9] → 匹配以“13-19”开头的手机号 |
| $ | 行结尾(单行模式:整个字符串结尾;多行模式:每行结尾) | \d{9}$ → 匹配以9个数字结尾的手机号 |
| \b | 单词边界(“单词字符”与“非单词字符”的间隙) | \bhello\b → 匹配“hello world”中的“hello”,不匹配“helloworld” |
| \B | 非单词边界(“单词字符”之间的间隙) | \Bhello\B → 匹配“ahellob”中的“hello”,不匹配“hello” |
| \G | 上一个匹配的结束位置 | 用于连续匹配(如提取逗号分隔的数字:\G\d+,?) |
4. 分组与捕获:将模式视为整体
用()将多个字符分组,可实现“重复整个组”“捕获组内容”“反向引用”等功能。
- (1) 捕获组:默认分组,可引用
用()包裹的组会被“捕获”,按左括号顺序编号(1、2、3…),可通过反向引用(\n,n为组号)复用捕获的内容。
- 示例:匹配重复的单词(如“hello hello”)
正则:\b(\w+)\b\s+\1\b
解析:(\w+)捕获单词(组1),\1引用组1的内容,匹配“hello hello”“test test”。
- (2) 非捕获组:仅分组,不捕获
用(?:)包裹,仅将字符视为整体,不分配组号(节省内存,无需引用时用)。
- 示例:匹配“ab”或“cd”重复2次
正则:(?:ab|cd){2} → 匹配“abab”“cdcd”“abcd”(非捕获组不占组号)。
- (3) 命名捕获组:用名称引用,更清晰
用(?<name>pattern)命名组,引用时用\k<name>(正则中)或$name(替换时,如JavaScript)。
- 示例:匹配日期(年-月-日)并引用年、月
正则:(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})
引用:\k<year>年\k<month>月\k<day>日 → 将“2024-05-20”转为“2024年05月20日”。
5. 断言:零宽预查/回顾后发
断言是“零宽”的(不消耗字符),仅判断“当前位置前后是否满足条件”,常用于复杂验证(如密码强度、格式限制)。
| 断言类型 | 语法 | 含义 | 示例 |
|---|
| 正向预查 | (?=pattern) | 匹配“后面满足pattern”的位置 | \d+(?=元) → 匹配“100元”中的“100”(后面必须是“元”) |
| 负向预查 | (?!pattern) | 匹配“后面不满足pattern”的位置 | \d+(?!元) → 匹配“100美元”中的“100”(后面不能是“元”) |
| 正向回顾后发 | (?<=pattern) | 匹配“前面满足pattern”的位置 | (?<=¥)\d+ → 匹配“¥100”中的“100”(前面必须是“¥”) |
| 负向回顾后发 | (?<!pattern) | 匹配“前面不满足pattern”的位置 | (?<!¥)\d+ → 匹配“$100”中的“100”(前面不能是“¥”) |
- 经典示例:密码强度验证(8-16位,含大小写字母、数字、特殊符号)
正则:^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[!@#$%^&*]).{8,16}$
解析:
(?=.*[A-Z]):后面必须有至少一个大写字母(?=.*[a-z]):后面必须有至少一个小写字母(?=.*\d):后面必须有至少一个数字(?=.*[!@#$%^&*]):后面必须有至少一个特殊符号.{8,16}:长度8-16位^$:确保整个字符串匹配(避免部分匹配)
6. 模式修饰符:调整正则行为
修饰符用于改变正则的匹配规则(如大小写不敏感、多行模式),不同语言语法略有差异(如JavaScript用/pattern/flags,Python用re.compile(pattern, flags))。
| 修饰符 | 含义 | 示例 |
|---|
| i | 忽略大小写(case-insensitive) | /abc/i → 匹配“ABC”“aBc” |
| g | 全局匹配(global):匹配所有符合条件的结果,而非第一个 | /a/g → 匹配“aaa”中的3个“a” |
| m | 多行模式(multiline):^匹配每行开头,$匹配每行结尾 | /^a/m → 匹配“123\na456”中的“a” |
| s | 单行模式(dotall):.匹配包括换行符在内的任意字符 | /a.b/s → 匹配“a\nb” |
| u | Unicode模式:支持Unicode字符(如中文字符、emoji) | /\p{Han}/u → 匹配中文字符 |
二、常用场景示例
1. 验证类(需加^$确保完整匹配)
| 场景 | 正则表达式 | 说明 |
|---|
| 手机号(中国大陆) | ^1[3-9]\d{9}$ | 1开头,第二位3-9,后面9位数字 |
| 邮箱(简化版) | ^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$ | 本地部分(字母/数字/_/-)+@+域名+顶级域名(如.com.cn) |
| 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]$ | 地区码6位+年份19/20开头+月份01-12+日期01-31+顺序码3位+校验码(0-9/X) |
| URL(简化版) | ^https?://[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}(/\S*)?$ | 支持http/https,域名+路径 |
2. 提取类(无需^$,匹配部分内容)
| 场景 | 正则表达式 | 说明 |
|---|
| 提取HTML标签内容 | <title>(.*?)</title> | 非贪婪匹配<title>标签内的内容 |
| 提取URL中的域名 | https?://([a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}) | 捕获组1为域名(如“baidu.com”) |
| 提取数字 | \d+ | 提取所有连续数字 |
3. 替换类(用捕获组或断言调整格式)
| 场景 | 正则 | 替换为 | 效果 |
|---|
| 日期格式转换(2024-05-20 → 2024年05月20日) | (\d{4})-(\d{2})-(\d{2}) | $1年$2月$3日 | “2024-05-20”→“2024年05月20日” |
| 手机号脱敏(13800138000 → 138****8000) | (\d{3})\d{4}(\d{4}) | $1****$2 | “13800138000”→“138****8000” |
| 去除字符串前后空格 | `^\s+ | \s+$` | (空字符串) |
三、常见误区与注意事项
- 忘记
^$导致部分匹配:验证时必须加^$,否则会匹配“包含目标模式”的字符串(如用1[3-9]\d{9}验证手机号,会匹配“a13800138000b”)。 - 贪婪量词滥用:提取短内容时用非贪婪量词(如
.*?),避免匹配过长(如提取<a>标签用<a>.*?</a>,而非<a>.*</a>)。 .不匹配换行符:需匹配换行时加s修饰符(如JavaScript的/a.b/s匹配“a\nb”)。- 转义错误:在字符串中写正则时,
\需转义为\\(如JavaScript中匹配“.”需写/a\.b/,Python中需写r'a\.b'或'a\\.b')。 - 过度复杂的正则:如邮箱验证,无需追求“完美匹配所有RFC标准”。