正则表达式(Regular Expression,简称Regex) 是一种强大的文本处理工具,用于在字符串中匹配、查找、替换特定模式的文本。本教程将从零开始,带你系统掌握这项核心技能。
第一部分:基础入门
1.1 什么是正则表达式?
正则表达式是一个模式字符串 ,描述了一类文本的共同特征 。就像用"通配符"*.txt匹配所有文本文件一样,但功能强大百倍。
实际应用场景:
- 验证用户输入(邮箱、手机号、密码强度)
- 提取网页中的数据(爬虫)
- 批量文本处理(查找替换)
- 日志分析
- 代码重构
1.2 你的第一个正则表达式
最简单的模式:字面匹配
hello
-
匹配字符串中完全包含
"hello"的部分 -
示例:
"hello world"→ 匹配到"hello" -
默认区分大小写,
"Hello"不匹配
在线工具推荐(边学边练):
第二部分:元字符与字符类(核心基础)
2.1 特殊字符(元字符)
这些字符在正则中有特殊含义:
| 元字符 | 含义 | 示例 | 匹配 |
|---|---|---|---|
. |
匹配任意单个字符(除换行符) | a.c |
"abc", "a2c", "a c" |
\d |
数字(等价于[0-9]) |
\d\d |
"12", "45" |
\w |
单词字符(字母、数字、下划线) | \w+ |
"hello", "user_123" |
\s |
空白字符(空格、制表符、换行) | a\sb |
"a b" |
\D |
非数字 | \D+ |
"abc", "---" |
\W |
非单词字符 | \W |
"@", " " |
\S |
非空白字符 | \S+ |
"hello" |
2.2 自定义字符类
用[]定义你想匹配的字符集合:
| 表达式 | 含义 | 匹配示例 | 不匹配 |
|---|---|---|---|
[abc] |
匹配a、b、c中的任意一个 | "a", "b" |
"d" |
[a-z] |
匹配a到z的任意小写字母 | "c", "m" |
"A", "1" |
[A-Za-z] |
匹配任意大小写字母 | "a", "Z" |
"1", "_" |
[0-9] |
匹配数字(同\d) |
"5" |
"a" |
[^abc] |
否定 :匹配不是a、b、c的字符 | "d", "1" |
"a", "b" |
[a-zA-Z0-9_] |
匹配单词字符(同\w) |
"a", "1", "_" |
"@" |
实用示例:
# 匹配16进制颜色值(如#FF00FF)
#[0-9A-Fa-f]{6}
# 匹配中文字符(在支持Unicode的环境中)
[\u4e00-\u9fa5]
第三部分:量词与重复(控制匹配次数)
3.1 基本量词
| 量词 | 含义 | 示例 | 匹配 |
|---|---|---|---|
* |
匹配0次或多次(尽可能多) | a* |
"", "a", "aaaa" |
+ |
匹配1次或多次(尽可能多) | \d+ |
"1", "123" |
? |
匹配0次或1次(可有可无) | colou?r |
"color", "colour" |
{n} |
匹配恰好n次 | \d{4} |
"2024"(4位数字) |
{n,} |
匹配至少n次 | \w{3,} |
"hello", "abc123"(至少3个) |
{n,m} |
匹配n到m次 | \d{2,4} |
"12", "1234" |
3.2 贪婪 vs 懒惰(非贪婪)模式
这是正则表达式的核心难点,必须掌握!
# 示例文本
"<div>内容1</div><div>内容2</div>"
# 1. 贪婪模式(默认):尽可能多地匹配
<.*> # 匹配结果:"<div>内容1</div><div>内容2</div>"
# 匹配了整个字符串,因为.*会吃到最后一个>才停止
# 2. 懒惰模式(加?):尽可能少地匹配
<.*?> # 匹配结果:第一次"<div>",第二次"</div>",第三次"<div>",第四次"</div>"
# 遇到第一个>就停止匹配
所有量词都有对应的懒惰版本:
*?、+?、??、{n,}?、{n,m}?
第四部分:分组、分支与引用
4.1 分组 ()
用小括号()将模式分组:
# 分组示例
(ab)+ # 匹配:"ab", "ababab"(ab重复)
(gr(a|e)y) # 匹配:"gray" 或 "grey"
分组的作用:
-
量化重复 :
(ab){3}→"ababab" -
多选分支 :
gray|grey可写作gr(a|e)y -
捕获文本:用于提取或替换
4.2 分支 |
相当于逻辑"或":
# 匹配多种可能性
cat|dog|fish # 匹配"cat"或"dog"或"fish"
gr(a|e)y # 匹配"gray"或"grey"
# 注意范围:会匹配整个左侧或右侧
^I like (cats|dogs)\.$ # 匹配"I like cats." 或 "I like dogs."
4.3 引用分组
用\1、\2等引用前面捕获的分组:
# 匹配重复的单词
(\w+) \1 # 匹配:"hello hello", "the the"
# \1代表第一个分组匹配到的内容
# 匹配成对的HTML标签
<(\w+)>.*?</\1> # 匹配:<div>内容</div> 但不匹配 <div>内容</span>
# \1确保结束标签与开始标签同名
第五部分:锚点与边界(精确定位)
5.1 常用锚点
| 锚点 | 含义 | 示例 | 匹配位置 |
|---|---|---|---|
^ |
字符串开始(多行模式下行首) | ^Hello |
必须以Hello开头 |
$ |
字符串结束(多行模式下行尾) | world$ |
必须以world结尾 |
\b |
单词边界(\w和\W之间) | \bcat\b |
单独的"cat",不匹配"category" |
\B |
非单词边界 | \Bcat\B |
"scattered"中的"cat" |
(?=...) |
正向肯定预查 | Windows(?=10) |
匹配后面是"10"的"Windows" |
(?!...) |
正向否定预查 | Windows(?!10) |
匹配后面不是"10"的"Windows" |
(?<=...) |
反向肯定预查 | (?<=\$)\d+ |
匹配前面是"$"的数字 |
(?<!...) |
反向否定预查 | (?<!\$)\d+ |
匹配前面不是"$"的数字 |
实用示例:
# 验证整个字符串是6位数字
^\d{6}$
# 查找单词"the"(不匹配"there")
\bthe\b
# 匹配金额数字(不带$符号)
(?<=\$)\d+\.\d{2} # 匹配:"$123.45"中的"123.45"
# 匹配不以"test_"开头的变量名
^(?!test_)\w+
第六部分:标志/修饰符(修改匹配行为)
在大多数编程语言中,正则表达式可以添加标志:
| 标志 | 含义 | 示例 |
|---|---|---|
i |
忽略大小写 | /hello/i匹配"Hello"、"HELLO" |
g |
全局匹配(查找所有) | 默认只匹配第一个,g标志匹配所有 |
m |
多行模式 | ^和$匹配每行的开头结尾 |
s |
单行模式 | .匹配包括换行符的所有字符 |
u |
Unicode模式 | 正确处理Unicode字符 |
使用示例(JavaScript):
javascript
// 不区分大小写 + 全局匹配
const regex = /hello/gi;
"Hello world, hello everyone".match(regex); // ["Hello", "hello"]
第七部分:实战应用(经典场景)
7.1 验证类
# 邮箱(简单版)
^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
# 中国大陆手机号
^1[3-9]\d{9}$
# 身份证号(18位)
^\d{17}[\dXx]$
# 日期(YYYY-MM-DD)
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
# 强密码(8-20位,含大小写字母、数字、特殊字符)
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,20}$
7.2 提取类
# 提取HTML标签内容
<tag[^>]*>(.*?)</tag>
# 提取URL中的域名
https?://([^/]+)
# 提取CSV字段
"([^"]*)"|([^,]+) # 处理带/不带引号的字段
# 提取所有中文
[\u4e00-\u9fa5]+
7.3 替换类
# 删除HTML标签
<[^>]*>
# 删除多余空白行
\n\s*\n
# 格式化电话号码(10位)
(\d{3})(\d{4})(\d{4}) → $1-$2-$3
第八部分:性能优化与最佳实践
8.1 效率优化技巧
-
减少回溯 :避免
(.*)*这样的嵌套量词 -
使用具体字符类 :用
[a-z]代替.*?中的? -
避免捕获分组 :用
(?:...)非捕获分组 -
尽早失败:在开头使用锚点或具体字符
-
懒惰匹配要谨慎 :
.*?可能导致大量回溯
8.2 最佳实践
-
注释复杂正则:
(?x) # 启用注释模式
^(\d{4}) # 年
-(\d{2}) # 月
-(\d{2}) # 日
$ -
测试驱动开发:
-
准备测试用例(包括边界情况)
-
使用在线工具逐步调试
-
编写单元测试
-
-
可读性优先:
不好
^\d{4}-\d{2}-\d{2}$
更好(带分组命名)
^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$
第九部分:各语言差异速查
| 功能 | JavaScript | Python | Java | PHP |
|---|---|---|---|---|
| 创建正则 | /pattern/flags |
re.compile(r"pattern", flags) |
Pattern.compile("pattern") |
/pattern/flags |
| 忽略大小写 | i |
re.IGNORECASE |
Pattern.CASE_INSENSITIVE |
i |
| 多行模式 | m |
re.MULTILINE |
Pattern.MULTILINE |
m |
| 全局匹配 | g |
默认全局 | 默认全局 | g |
| 命名分组 | (?<name>...) |
(?P<name>...) |
(?<name>...) |
(?P<name>...) |
第十部分:调试与工具
10.1 调试步骤
-
明确需求:精确描述你要匹配什么
-
分步构建:从简单到复杂逐步添加
-
测试用例:包括应该匹配和不应该匹配的例子
-
优化性能:检查是否有回溯问题
10.2 推荐工具
-
Regex101:交互式测试,有详细解释
-
RegExr:实时高亮,支持社区分享
-
Regex Tester:VSCode插件,集成开发环境
-
Debuggex:可视化正则表达式
结语
正则表达式是实践出真知的技能。建议你:
-
从简单的模式开始,逐步增加复杂度
-
为每个正则写测试用例,包括边界情况
-
理解原理,不要死记硬背
-
收藏本教程,遇到问题时快速查阅
练习是最好的老师,现在打开Regex101,从最简单的模式开始尝试吧!