正则表达式完全指南
什么是正则表达式
正则表达式(Regular Expression)是一种描述字符串匹配模式的工具,由普通字符和特殊字符(元字符)构成。它广泛应用于文本搜索、数据验证、内容提取等场景,是跨平台、跨语言的标准化技术。
推荐一个实用的在线测试工具:regex101.com,支持实时调试和可视化分析。
模式修饰符
修饰符用于控制匹配行为,通常放在表达式末尾。以下是四个核心修饰符:
全局匹配(g)
默认情况下,正则只返回第一个匹配结果。开启全局模式后,会返回所有匹配项。
多行模式(m)
影响 ^ 和 $ 的行为:
- 关闭时:
^匹配整个文本开头,$匹配整个文本结尾 - 开启后:
^可匹配任意行首,$可匹配任意行尾
单行模式(s)
默认情况下,点号 . 不匹配换行符 \n。开启后,. 可匹配包括换行符在内的任意字符。
忽略大小写(i)
匹配时不区分英文字母大小写,a 与 A 视为等价。
字符匹配基础
字面量匹配
直接书写目标字符串即可匹配,如 export 可匹配文本中的 "export"。
预定义字符类
| 符号 | 含义 | 等价写法 |
|---|---|---|
. | 除换行外的任意字符 | — |
\d | 数字字符 | [0-9] |
\D | 非数字字符 | [^0-9] |
\w | 单词字符(字母、数字、下划线) | [a-zA-Z0-9_] |
\W | 非单词字符 | [^a-zA-Z0-9_] |
\s | 空白字符(空格、制表符、换行等) | — |
\S | 非空白字符 | — |
\b | 单词边界 | — |
\B | 非单词边界 | — |
自定义字符集
使用方括号 [] 定义匹配范围:
[0-9a-f]:匹配十六进制字符[^...]:匹配不在方括号内的字符(取反)[\^$.]:匹配字面量^、$、.(特殊字符需转义)
数量控制
精确量词
{n}:恰好出现 n 次{n,}:至少出现 n 次{n,m}:出现 n 到 m 次
简写量词
| 符号 | 含义 | 等价形式 |
|---|---|---|
* | 零次或多次 | {0,} |
+ | 一次或多次 | {1,} |
? | 零次或一次 | {0,1} |
贪婪与惰性匹配
默认量词为贪婪模式,尽可能多地匹配字符。在量词后添加 ? 可切换为惰性模式,尽可能少地匹配。
示例:匹配 HTML 中的 span 标签
<span>内容1</span>文本<span>内容2</span>
// 贪婪模式:<span>.*?</span> 匹配整个字符串(错误)
// 惰性模式:<span>.*?</span> 分别匹配两个标签(正确)
分组与捕获
基础分组
圆括号 () 用于创建分组,可实现:
- 信息提取:通过
$1、$2等引用匹配内容 - 反向引用:在表达式内部用
\1、\2引用前面分组
示例:匹配对称的 HTML 标签
<(h[1-6])>.*?</\1>
高级分组特性
| 语法 | 说明 |
|---|---|
(?<name>...) | 命名分组,可通过名称而非序号访问 |
(?:...) | 非捕获分组,仅用于逻辑分组不保存结果 |
((...)) | 嵌套分组,按左括号顺序编号 |
大小写转换
部分编辑器支持替换时的特殊操作符:
\u/\U:转大写\l/\L:转小写\E:结束大小写转换
边界断言(零宽断言)
断言用于判断位置条件,不消耗匹配字符:
| 语法 | 名称 | 说明 |
|---|---|---|
(?=...) | 先行肯定断言 | 后面紧跟指定内容 |
(?!...) | 先行否定断言 | 后面不跟指定内容 |
(?<=...) | 后行肯定断言 | 前面存在指定内容 |
(?<!...) | 后行否定断言 | 前面不存在指定内容 |
应用示例:排除表头匹配姓名
(?<!姓名\s)\b\w+\b
Java 正则 API
Java 通过 java.util.regex 包提供正则支持,核心类为 Pattern 和 Matcher。
Pattern 类
负责编译正则表达式:
// 编译模式
Pattern pt = Pattern.compile("\\d+");
// 快速匹配验证
boolean isMatch = Pattern.matches("\\d+", "12345");
// 分割字符串
String[] parts = pt.split("a1b2c3d");
Matcher 类
执行具体的匹配操作:
Pattern pt = Pattern.compile("\\bcat\\b");
Matcher mc = pt.matcher("cat catalog cat");
// 查找所有匹配
while (mc.find()) {
System.out.println("位置: " + mc.start() + "-" + mc.end());
}
// 匹配检查
boolean fullMatch = mc.matches(); // 要求完全匹配
boolean prefixMatch = mc.lookingAt(); // 要求开头匹配
// 替换操作
String resultAll = mc.replaceAll("dog"); // 替换所有
String resultFirst = mc.replaceFirst("dog"); // 仅替换首个
综合案例
案例一:车牌号校验
规则:首位汉字 + 次位大写字母(不含 I、O)+ 五位字母数字(不含 I、O)
[\u4e00-\u9fa5][A-HJ-NP-Z][A-HJ-NP-Z0-9]{5}
案例二:密码强度验证
要求:8-20 位,必须同时包含大小写字母和数字
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,20}$
案例三:千分位格式化
为数字添加千分位分隔符:
查找:(?<=\d)(?=(\d{3})+$)
替换:,
案例四:提取 JavaScript 字符串
匹配单引号、双引号、反引号包裹的字符串:
(["'`])(?:(?=(\\?))\2.)*?\1
案例五:移除 HTML 注释
<!--[\s\S]*?-->