正则表达式(Regex)完整指南
# 🔍 正则表达式完整指南
适合人群:程序员、数据分析师、文本处理工程师
学习时间:2-3小时
前置知识:基础编程概念
正则表达式(Regular Expression,简称regex)是一种强大的文本模式匹配工具,广泛应用于字符串处理、数据验证、文本搜索等领域。
# 📚 你将学到什么
- 基础语法 - 掌握正则表达式的核心概念和语法
- 实际应用 - 学会解决常见的文本处理问题
- 高级技巧 - 理解捕获分组、前瞻断言等高级概念
- 最佳实践 - 掌握性能优化和调试技巧
- 工具推荐 - 了解常用的正则表达式工具
# 🎯 学习路径建议
# 📖 目录
# 🔤 基础语法
# 1. 基本字符
| 字符 | 说明 | 示例 |
|---|---|---|
. | 除换行符以外的所有字符 | a.c 匹配 "abc", "a1c" |
^ | 字符串开头 | ^hello 匹配以"hello"开头的字符串 |
$ | 字符串结尾 | world$ 匹配以"world"结尾的字符串 |
\ | 转义字符 | \. 匹配字面意思的点号 |
# 2. 字符类
| 字符类 | 说明 | 示例 |
|---|---|---|
\d | 匹配数字 (0-9) | \d+ 匹配一个或多个数字 |
\w | 匹配字母、数字、下划线 | \w+ 匹配一个或多个单词字符 |
\s | 匹配空白字符 | \s+ 匹配一个或多个空格 |
\D | 匹配非数字 | \D+ 匹配一个或多个非数字 |
\W | 匹配非单词字符 | \W+ 匹配一个或多个非单词字符 |
\S | 匹配非空白字符 | \S+ 匹配一个或多个非空白字符 |
# 3. 字符集合
| 模式 | 说明 | 示例 |
|---|---|---|
[abc] | 匹配 a、b 或 c 中的一个字母 | [abc] 匹配 "a", "b", "c" |
[a-z] | 匹配 a 到 z 中的一个字母 | [a-z]+ 匹配小写字母 |
[A-Z] | 匹配 A 到 Z 中的一个字母 | [A-Z]+ 匹配大写字母 |
[0-9] | 匹配 0 到 9 中的一个数字 | [0-9]+ 匹配数字 |
[^abc] | 匹配除了 a、b 或 c 中的其他字母 | [^abc] 匹配除abc外的字符 |
[\u4e00-\u9fa5] | 匹配中文字符 | [\u4e00-\u9fa5]+ 匹配中文 |
# 4. 量词
| 量词 | 说明 | 示例 |
|---|---|---|
? | 0 次或 1 次匹配 | colou?r 匹配 "color" 或 "colour" |
* | 匹配 0 次或多次 | runoo*b 匹配 "runob", "runoob", "runooob" |
+ | 匹配 1 次或多次 | runoo+b 匹配 "runoob", "runooob" |
{n} | 匹配 n 次 | \d{3} 匹配恰好3个数字 |
{n,} | 匹配 n 次以上 | \d{3,} 匹配3个或更多数字 |
{m,n} | 最少 m 次, 最多 n 次匹配 | \d{3,5} 匹配3到5个数字 |
# 5. 分组和引用
| 模式 | 说明 | 示例 |
|---|---|---|
(expr) | 捕获 expr 子模式,以 \1 使用它 | (\w+)\s\1 匹配重复的单词 |
(?:expr) | 忽略捕获的子模式 | (?:abc\|def) 匹配但不捕获 |
(?=expr) | 正向预查模式 expr | \w+(?=\s) 匹配后面跟空格的单词 |
(?!expr) | 负向预查模式 expr | \w+(?!\s) 匹配后面不跟空格的单词 |
# 6. 选择
| 模式 | 说明 | 示例 |
|---|---|---|
aa\|bb | 匹配 aa 或 bb | cat\|dog 匹配 "cat" 或 "dog" |
# case
runoo+b, 可以匹配 runoob、runooob、runoooooob 等, + 号代表前面的字符必须至少出现一次(1 次或多次)尝试一下runoo*b, 可以匹配 runob、runoob、runoooooob 等,*号代表前面的字符可以不出现, 也可以出现一次或者多次(0 次、或 1 次、或多次)尝试一下colou?r可以匹配 color 或者 colour, ? 问号代表前面的字符最多只可以出现一次(0 次或 1 次)尝试一下colorChangeConfig(?!=undefined): 包含 colorChangeConfig, 不包含=undefined
# 负向前瞻断言
/((?!api|_next/static|_next/image|.*\.png$).*)
((?!...).*): 这是一个负向前瞻断言, 它表示后面的表达式不应该匹配某些模式?!api|_next/static|_next/image|.*\.png$: 这是一个子表达式, 包含了要排除的模式列表api: 排除了路径中包含 api 的情况_next/static: 排除了路径中包含 _next/static 的情况_next/image: 排除了路径中包含 _next/image 的情况.*\.png$: 排除了路径以 .png 结尾的情况
:(?! ): 选择:且后面的字符不是空格的
因此, 这个正则表达式的含义是: 匹配所有不包含 api、_next/static、_next/image 和以 .png 结尾的路径。换句话说, 中间件将会在这些路径之外的所有路径上执行
# 复合
^\d\.\s[^\x00-\x7F]+: $: 单数字开头, 随后是一个点号。 后面是空格, 随后是若干个非 ASCII 字符, 最后是一个冒号(^[^\x00-\x7F]+) ([a-zA-Z]) ([^\x00-\x7F]):: 匹配类似 '按下 i 键: '([a-zA-Z.]+): 匹配连续的字母和.(^\w.*):: 字符开始, 到:(^[^\s]+):: 非空格连续^\d\. (.*?):: 如果一行中带多个:,只想匹配到第一个: 截止, (2. aaa: bbb:)
# other
# 捕获分组和非捕获分组的区别
理解非捕获分组的关键在于了解正则表达式中捕获分组和非捕获分组的区别
捕获分组和非捕获分组的区别- 捕获分组: 用圆括号
()包围的部分,匹配的内容会被捕获,可以通过反向引用或者程序来提取这些匹配的内容 - 非捕获分组: 用
(?:...)包围的部分,匹配的内容不会被捕获,不会影响分组的编号,也不能通过反向引用来提取内容
假设我们有以下字符串和正则表达式:
字符串: "JsApiTransformWork and JsApiTransformTask"
正则表达式: (\w+)(Work|Task)
2
这个正则表达式中包含两个捕获分组:
(\w+)捕获字母或数字的连续字符串(Work|Task)捕获 Work 或 Task
如果你在代码中使用这个正则表达式来匹配字符串,例如在 JavaScript 中:
const regex = /(\w+)(Work|Task)/g;
const str = 'JsApiTransformWork and JsApiTransformTask';
const matches = [...str.matchAll(regex)];
console.log(matches);
2
3
4
5
结果会是这样的:
[
['JsApiTransformWork', 'JsApiTransform', 'Work'],
['JsApiTransformTask', 'JsApiTransform', 'Task'],
];
2
3
4
这里 matches 包含两个匹配结果,每个匹配结果包含三个部分:
整个匹配的字符串,如 "JsApiTransformWork". 第一个捕获分组的内容,如 "JsApiTransform". 第二个捕获分组的内容,如 "Work" 或 "Task".
使用非捕获分组如果你使用非捕获分组 (?:Work|Task),正则表达式如下:
(\w+)(?:Work|Task)
再次运行上面的代码,结果会变成这样:
[
['JsApiTransformWork', 'JsApiTransform'],
['JsApiTransformTask', 'JsApiTransform'],
];
2
3
4
这里 matches 仍然包含两个匹配结果,但每个匹配结果只有两个部分:
- 整个匹配的字符串,如 "JsApiTransformWork".
- 第一个捕获分组的内容,如 "JsApiTransform".
这次 Work 或 Task 的匹配结果没有被捕获,也没有出现在 matches 数组中
总结使用 (?:Work|Task) 非捕获分组,意味着我们只想匹配 Work 或 Task,但不需要在匹配结果中提取或引用它们。这样可以简化结果,并且有助于避免不必要的捕获分组
# 引用捕获组索引
了解 $1
正则表达式中的 $1 不是用于匹配文本的模式, 而是用于在正则表达式替换中引用匹配组(捕获组)的特殊语法。它通常用于替换操作, 以便在替换文本中使用匹配的内容
在正则表达式替换中, 通常使用圆括号 () 来创建捕获组, 然后可以使用 $1、$2、$3 等来引用这些捕获组, 以在替换文本中插入匹配的内容。每个 $n 表示引用第 n 个捕获组的内容
例如, 假设我们有以下文本:
Name: John, Age: 30
如果我们想要提取名字和年龄, 并在替换文本中使用它们, 可以使用正则表达式:
Name: (\w+), Age: (\d+)
在替换文本中, 我们可以使用 $1 来引用第一个捕获组(名字), 使用 $2 来引用第二个捕获组(年龄)。例如, 可以将其替换为:
Hello, $1! You are $2 years old.
替换后的文本将是:
Hello, John! You are 30 years old.
请注意, $1 在正则表达式模式中不是一个有效的匹配, 而是在替换操作中用于引用捕获组的方式
# ⚡ 性能优化
# 1. 避免回溯灾难
问题示例:
(a+)+b
当匹配 "aaaaaaaaac" 时,会产生大量回溯。
优化方案:
a+b
# 2. 使用非捕获分组
避免不必要的捕获:
# 不好的做法
(abc|def)
# 好的做法
(?:abc|def)
2
3
4
5
# 3. 使用原子分组
防止回溯:
# 原子分组(某些引擎支持)
(?>a+)+b
2
# 4. 优化字符类
使用更精确的字符类:
# 不好的做法
.*
# 好的做法
[^<]*
2
3
4
5
# 🛠️ 工具推荐
# 在线工具
Regex101 - https://regex101.com/ (opens new window)
- 支持多种正则表达式引擎
- 实时测试和调试
- 详细的解释和匹配信息
RegExr - https://regexr.com/ (opens new window)
- 交互式学习工具
- 内置常用模式库
- 支持JavaScript正则表达式
RegexPal - https://www.regexpal.com/ (opens new window)
- 简单易用的在线测试工具
- 支持JavaScript正则表达式
# 编程语言工具
# JavaScript
// 测试正则表达式
const regex = /\d{3}-\d{3}-\d{4}/;
const phone = "123-456-7890";
console.log(regex.test(phone)); // true
// 提取匹配内容
const matches = phone.match(regex);
console.log(matches[0]); // "123-456-7890"
2
3
4
5
6
7
8
# Python
import re
# 测试正则表达式
pattern = r'\d{3}-\d{3}-\d{4}'
phone = "123-456-7890"
match = re.search(pattern, phone)
if match:
print(match.group()) # "123-456-7890"
2
3
4
5
6
7
8
# Java
import java.util.regex.Pattern;
import java.util.regex.Matcher;
Pattern pattern = Pattern.compile("\\d{3}-\\d{3}-\\d{4}");
Matcher matcher = pattern.matcher("123-456-7890");
if (matcher.find()) {
System.out.println(matcher.group()); // "123-456-7890"
}
2
3
4
5
6
7
8
# ❓ 常见问题
# Q1: 如何匹配多行文本?
A: 使用多行模式标志:
# JavaScript
/^start.*end$/m
# Python
re.MULTILINE
2
3
4
5
# Q2: 如何匹配特殊字符?
A: 使用转义字符:
# 匹配点号
\.
# 匹配方括号
\[
\]
2
3
4
5
6
# Q3: 如何匹配Unicode字符?
A: 使用Unicode范围:
# 匹配中文字符
[\u4e00-\u9fa5]
# 匹配emoji
[\u{1F600}-\u{1F64F}]
2
3
4
5
# Q4: 如何优化正则表达式性能?
A: 优化建议:
- 避免过度使用
.* - 使用非捕获分组
(?:...) - 使用原子分组防止回溯
- 优先使用字符类而不是选择
# Q5: 如何处理复杂的嵌套结构?
A: 对于复杂的嵌套结构,正则表达式可能不是最佳选择:
- 考虑使用专门的解析器
- 或者分步骤处理
- 使用递归正则表达式(如果引擎支持)
# 📚 学习资源
# 官方文档
# 在线教程
# 在线工具
- Regex101 (opens new window) - 功能强大的在线测试工具
- RegExr (opens new window) - 交互式学习工具
- RegexPal (opens new window) - 简单易用的测试工具
# 🎉 总结
通过本指南的学习,你已经掌握了:
- 基础语法 - 理解了正则表达式的核心概念和语法规则
- 实际应用 - 学会了解决常见的文本处理问题
- 高级技巧 - 掌握了捕获分组、前瞻断言等高级概念
- 性能优化 - 了解了如何编写高效的正则表达式
- 工具使用 - 熟悉了常用的正则表达式工具和调试方法
正则表达式是一个强大的工具,掌握它能够大大提高文本处理的效率。继续练习和实践,你将成为正则表达式的高手!🚀