正则表达式(Regular Expression)终极指南
一、正则表达式基础概念
1. 什么是正则表达式?
正则表达式(Regex)是一种用于匹配、查找、替换文本模式的字符串,由普通字符和特殊字符(元字符)组成。它被广泛应用于:
- 文本处理(搜索、替换、验证)
- 数据清洗(提取结构化信息)
- 编程语言(Python、JavaScript、Java等)
- 开发工具(IDE、文本编辑器、命令行工具)
2. 正则表达式组成
- 字面字符:普通字符(如
a
、1
、#
)直接匹配自身。
- 元字符:具有特殊含义的符号(如
.
、*
、^
)。
- 字符类:定义可匹配的字符集合(如
[a-z]
)。
- 量词:指定匹配次数(如
+
、{3,5}
)。
- 分组与捕获:将模式分组并捕获子匹配(如
(ab)+
)。
- 断言:指定匹配位置(如
^
、$
、\b
)。
二、元字符与转义
1. 基本元字符
元字符 |
含义 |
示例 |
. |
匹配任意单个字符(除换行符) |
a.c → “abc”, “a1c” |
^ |
匹配字符串开始 |
^Hello → 匹配行首的Hello |
$ |
匹配字符串结束 |
end$ → 匹配行末的end |
\ |
转义字符或特殊序列 |
\. 匹配字面点号 |
| |
逻辑或 |
cat|dog → 匹配cat或dog |
[...] |
字符类,匹配其中任意一个字符 |
[aeiou] → 匹配元音字母 |
[^...] |
否定字符类,匹配不在其中的字符 |
[^0-9] → 匹配非数字字符 |
2. 转义规则
- 转义元字符:使用
\
将元字符转为普通字符。
示例:\*
匹配星号,\\
匹配反斜杠。
- 预定义转义序列:
\n
:换行符
\t
:制表符
\r
:回车符
\d
:数字(等价于 [0-9]
)
\D
:非数字(等价于 [^0-9]
)
\s
:空白字符(空格、制表符等)
\S
:非空白字符
\w
:单词字符(字母、数字、下划线)
\W
:非单词字符
三、字符类与量词
1. 字符类
- 范围表示:
[a-z]
匹配小写字母,[A-Za-z0-9]
匹配字母数字。
- 预定义字符类:
[:alnum:]
:字母和数字(需在字符类内使用,如 [[:alnum:]]
)
[:alpha:]
:字母
[:digit:]
:数字
[:lower:]
:小写字母
[:upper:]
:大写字母
2. 量词
量词 |
含义 |
示例 |
* |
0次或多次 |
a* → “”, “a”, “aa” |
+ |
1次或多次 |
a+ → “a”, “aa” |
? |
0次或1次 |
a? → “”, “a” |
{n} |
精确匹配n次 |
a{3} → “aaa” |
{n,} |
至少n次 |
a{2,} → “aa”, “aaa” |
{n,m} |
匹配n到m次 |
a{2,4} → “aa”, “aaa” |
贪婪与非贪婪模式
- 贪婪模式(默认):尽可能匹配最长字符串。
示例:
a.*b
匹配 “aabcb” 中的整个字符串。
- 非贪婪模式(加
?
):尽可能匹配最短字符串。
示例:a.*?b
匹配 “aabcb” 中的 “aab” 和 “ab”。
四、分组与捕获
1. 分组
- 普通分组
( )
:将模式分组,可应用量词。
示例:(ab)+
→ “ababab”。
- 非捕获分组
(?: )
:分组但不捕获。
示例:(?:ab)+
匹配但不保存分组内容。
2. 捕获与引用
- 捕获组编号:从左到右按左括号顺序编号(从1开始)。
示例:
(\d{4})-(\d{2})
捕获年(组1)和月(组2)。
- 反向引用:使用
\n
引用分组内容(n
为组号)。
示例:(\w+) \1
匹配重复单词(如 “the the”)。
3. 命名分组
- 语法:
(?P<name>pattern)
(Python)或 (?<name>pattern)
(其他语言)。
示例:(?P<year>\d{4})
捕获名为 “year” 的组。
- 引用命名分组:
- Python:
\g<year>
- JavaScript:
\k<year>
五、断言(Lookaround)
1. 基本断言
断言 |
语法 |
含义 |
正向先行断言 |
(?=pattern) |
右侧必须匹配pattern |
负向先行断言 |
(?!pattern) |
右侧不能匹配pattern |
正向后行断言 |
(?<=pattern) |
左侧必须匹配pattern |
负向后行断言 |
(?<!pattern) |
左侧不能匹配pattern |
2. 示例
- 提取金额数字:
(?<=\$)\d+
→ 匹配 “$100” 中的 “100”。
- 密码强度验证:
至少包含大写字母、小写字母和数字:
^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).{8,}$
六、常见正则表达式模式
1. 基础验证
- 邮箱:
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
- URL:
^(https?://)?([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
- IPv4地址:
^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$
2. 数据提取
- HTML标签内容:
<h1>(.*?)</h1>
→ 匹配 <h1>标题</h1>
中的 “标题”。
- 日志时间戳:
\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}
→ 匹配 “2023-10-01 12:34:56”。
3. 替换与格式化
- 日期格式转换(YYYY/MM/DD → DD-MM-YYYY):
搜索:
(\d{4})/(\d{2})/(\d{2})
替换:\3-\2-\1
- 删除多余空格:
搜索:
\s+
替换:
(单个空格)
七、正则表达式性能优化
1. 避免回溯爆炸
- 具体化模式:尽量使用精确匹配,避免模糊量词(如
.*
)。
- 使用原子组(Atomic Groups):某些引擎支持(如
(?>pattern)
),禁止回溯。
2. 优化技巧
- 锚定开始/结束:使用
^
和 $
减少搜索范围。
- 优先选择非贪婪量词:在复杂模式中减少回溯。
- 避免嵌套量词:如
(a+)+
可能导致指数级回溯。
八、各语言中的正则表达式
1. Python
- 模块:
re
- 函数:
re.match()
:从字符串开始匹配
re.search()
:搜索整个字符串
re.findall()
:返回所有匹配结果
re.sub()
:替换匹配内容
- 标志:
re.IGNORECASE
(re.I
):忽略大小写
re.MULTILINE
(re.M
):多行模式
2. JavaScript
- 语法:
/pattern/flags
- 方法:
String.match()
String.replace()
RegExp.test()
- 标志:
3. Java
- 类:
java.util.regex.Pattern
和 Matcher
- 方法:
Pattern.compile()
Matcher.find()
Matcher.group()
九、工具与资源
1. 在线测试工具
2. 学习资源
十、正则表达式陷阱与最佳实践
1. 常见陷阱
- 过度匹配:如
.*
可能意外匹配超长文本。
- 未转义字符:如
+
、?
在字符类外需转义。
- 性能问题:复杂模式可能导致超时。
2. 最佳实践
- 注释与格式化:复杂正则使用
x
标志(忽略空格和注释)。
示例(Python):
1
2
3
4
5
|
pattern = re.compile(r'''
^\d{3} # 匹配前3位数字
-? # 可选短横线
\d{2} # 后2位数字
''', re.VERBOSE)
|
- 单元测试:为关键正则编写测试用例。
- 优先使用内置解析器:如解析HTML/XML时,正则可能不够健壮。
十一、高级技巧
1. 递归匹配
- 平衡括号匹配:
\((?:[^()]|(?R))*+\)
(支持递归的正则引擎如PCRE)。
2. 条件匹配
- 语法:
(?(condition)yes-pattern|no-pattern)
示例:匹配带区号的电话号码(可选区号):
1
|
^(\(\d{3}\) )?\d{3}-\d{4}$
|
3. 模式修饰符
- 内联修饰符:如
(?i)
忽略大小写,(?m)
多行模式。
示例:(?i)hello
匹配 “Hello”、“HELLO”。
十二、总结图表
正则表达式速查表
类别 |
语法 |
示例 |
字符类 |
[abc] , [^abc] |
[aeiou] , [^0-9] |
量词 |
* , + , ? , {n,m} |
a{2,4} , .*? |
分组与捕获 |
( ) , (?: ) , (?P<>) |
(\d+) , (?P<year>\d{4}) |
断言 |
(?= ) , (?<= ) |
(?=\d) , (?<=@)\w+ |
转义字符 |
\d , \s , \w |
\d{3}-\d{4} |