一、正则表达式核心规则
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标准”。