正则表达式教程:正则表达式初学者友好指南
· 12分钟阅读
目录
什么是正则表达式以及为什么要学习它们?
正则表达式(通常缩写为regex或regexp)是强大的模式匹配工具,允许您使用专门的语法搜索、验证、提取和操作文本。可以将它们视为一种复杂的搜索语言,远远超出简单的"查找和替换"操作。
想象一下,您需要从包含数千行日志数据的文件中提取所有电子邮件地址,或验证用户的电话号码是否符合正确的格式。使用传统的字符串操作方法会导致冗长、难以维护的代码。正则表达式可以用单个简洁的模式完成这些任务。
正则表达式的核心是使用文字字符和特殊元字符的组合来定义搜索模式。这些模式可以匹配简单的字符串(如"cat")或复杂的结构(如电子邮件地址、URL或信用卡号)。
为什么应该学习正则表达式?
- 文本处理效率:正则表达式可以快速处理大量文本数据,并执行需要数十行常规代码才能完成的复杂搜索和替换操作
- 数据验证:验证用户输入(电子邮件、电话号码、密码强度)是Web开发中的常见需求,正则表达式提供了优雅的解决方案
- 数据提取:从非结构化文本中提取结构化信息,例如从网页中提取链接或从日志中提取错误消息
- 跨平台通用性:几乎每种编程语言和文本编辑器都支持具有相似语法的正则表达式
- 提高生产力:掌握正则表达式可以大大减少编写重复代码和执行手动文本操作所花费的时间
- 代码重构:在重构项目期间快速查找和修改整个代码库中的模式
实际应用
正则表达式在软件开发和数据处理中被广泛使用:
- Web表单验证:确保电子邮件、电话号码、邮政编码和其他用户输入符合预期格式
- 日志分析:解析服务器日志以提取错误消息、IP地址、时间戳和其他相关数据
- 文本编辑器操作:在VS Code、Sublime Text或Vim等IDE中进行高级搜索和替换
- 网页抓取:在构建网络爬虫时从HTML内容中提取特定数据模式
- 配置文件解析:读取和验证具有特定语法要求的配置文件
- 数据清理:在分析之前标准化数据集中不一致的数据格式
- 安全性:检测用户输入中的恶意模式以防止注入攻击
专业提示:虽然正则表达式很强大,但它并不总是适用于每项工作的最佳工具。对于解析HTML或JSON等复杂结构化数据,请使用专用解析器。正则表达式最适合纯文本中的模式匹配。
基本语法和基础知识
正则表达式由两种类型的字符组成:文字字符(匹配自身)和元字符(具有特殊含义)。让我们从基础知识开始。
文字字符
最简单的正则表达式就是纯文本。模式cat将匹配文本中的确切字符串"cat"。
文本:"The cat sat on the mat"
正则表达式:cat
匹配:"The cat sat on the mat"
默认情况下,文字字符区分大小写,因此cat不会匹配"Cat"或"CAT",除非使用不区分大小写的标志。
点元字符(.)
点.是一个通配符,匹配除换行符之外的任何单个字符。
文本:"cat"、"cot"、"cut"、"c@t"
正则表达式:c.t
匹配:所有四个字符串
要匹配文字点字符,请使用反斜杠转义:\.
文本:"file.txt"
正则表达式:file\.txt
匹配:"file.txt"(不是"fileAtxt")
锚点:匹配位置
锚点不匹配字符——它们匹配文本中的位置。
插入符号(^)- 行首:^锚点匹配字符串或行的开头。
文本:"cat\ndog\ncat"
正则表达式:^cat
匹配:仅第一个"cat"
美元符号($)- 行尾:$锚点匹配字符串或行的结尾。
文本:"cat\ndog\ncat"
正则表达式:cat$
匹配:仅最后一个"cat"
组合锚点:同时使用两者来匹配整行。
正则表达式:^cat$
匹配:仅包含确切"cat"且前后没有其他内容的行
单词边界(\b)
\b锚点匹配单词边界——单词字符和非单词字符之间的位置。
文本:"cat category caterpillar"
正则表达式:\bcat\b
匹配:仅独立单词"cat"
这对于查找完整单词而不匹配部分单词非常有用。
转义序列
正则表达式中的特殊字符需要用反斜杠转义才能按字面意思匹配:
| 特殊字符 | 转义形式 |
|---|---|
| . * + ? ^ $ { } [ ] ( ) | \ | \. \* \+ \? \^ \$ \{ \} \[ \] \( \) \| \\ |
匹配价格的示例:
正则表达式:\$\d+\.\d{2}
匹配:"$19.99"、"$5.00"
字符类和范围
字符类允许您定义一组字符并匹配其中的任何一个。它们用方括号括起来。
基本字符类
方括号[]创建一个字符集,匹配其中的任何单个字符。
文本:"cat"、"cot"、"cut"、"cit"
正则表达式:c[aou]t
匹配:"cat"、"cot"、"cut"(不是"cit")
字符范围
使用连字符定义字符范围:
[a-z]- 任何小写字母[A-Z]- 任何大写字母[0-9]- 任何数字[a-zA-Z]- 任何字母(大写或小写)[a-z0-9]- 任何字母或数字
文本:"a1"、"b2"、"c3"、"d4"
正则表达式:[a-c][1-3]
匹配:"a1"、"b2"、"c3"(不是"d4")
否定字符类
在字符类的开头使用插入符号^来否定它——匹配不在集合中的任何字符。
正则表达式:[^0-9]
匹配:任何不是数字的字符
文本:"abc123def"
正则表达式:[^a-z]+
匹配:"123"(非小写字母的序列)
预定义字符类
正则表达式为常见字符类提供了简写:
| 简写 | 等效 | 描述 |
|---|---|---|
\d |
[0-9] |
任何数字 |
\D |
[^0-9] |
任何非数字 |
\w |
[a-zA-Z0-9_] |
任何单词字符 |
\W |
[^a-zA-Z0-9_] |
任何非单词字符 |
\s |
[ \t\n\r\f\v] |
任何空白字符 |
\S |
[^ \t\n\r\f\v] |
任何非空白字符 |
匹配简单电话号码的示例:
正则表达式:\d{3}-\d{3}-\d{4}
匹配:"555-123-4567"
快速提示:简写类的大写版本始终是其小写对应项的否定。\d匹配数字,\D匹配非数字。
量词:控制匹配重复
量词指定字符或组应匹配多少次。它们放在要重复的元素之后。
基本量词
*- 零次或多次+- 一次或多次?- 零次或一次(使某物可选){n}- 恰好n次{n,}- 至少n次{n,m}- n到m次之间
量词实际应用示例
星号(*)- 零次或多次:
正则表达式:ca*t
匹配:"ct"、"cat"、"caat"、"caaat"
加号(+)- 一次或多次:
正则表达式:ca+t
匹配:"cat"、"caat"、"caaat"(不是"ct")
问号(?)- 可选:
正则表达式:colou?r
匹配:"color"和"colour"
精确计数{n}:
正则表达式:\d{3}
匹配:恰好三位数字,如"123"
范围{n,m}:
正则表达式:\d{2,4}
匹配:2到4位数字,如"12"、"123"或"1234"
贪婪量词与懒惰量词
默认情况下,量词是贪婪的——它们匹配尽可能多的文本。在量词后添加?使其变为懒惰(匹配尽可能少的文本)。
文本:"<div>content</div><div>more</div>"
正则表达式(贪婪):<div>.*</div>
匹配:"<div>content</div><div>more</div>"(整个字符串)
正则表达式(懒惰):<div>.*?</div>
匹配:"<div>content</div>"(仅第一个标签)
懒惰量词:
*?- 零次或多次(懒惰)+?- 一次或多次(懒惰)??- 零次或一次(懒惰){n,m}?- n到m次之间(懒惰)
专业提示:贪婪匹配可能会导致大文本的性能问题。当您需要匹配最短可能的字符串时,请使用懒惰量词,特别是在处理嵌套结构时。
实际示例:匹配HTML标签
正则表达式:<([a-z]+)>.*?</\1>
匹配:成对的HTML标签,如"<p>text</p>"或"<div>content</div>"
此模式使用懒惰匹配来避免一次捕获多个标签,并使用反向引用(下面介绍)来确保开始和结束标签匹配。
分组和捕获
括号()创建具有多种用途的组:它们将模式的各部分组合在一起,捕获匹配的文本以供以后使用,并启用反向引用。
基本分组
组允许您将量词应用于多个字符:
正则表达式:(ha)+
匹配:"ha"、"haha"、"hahaha"
如果没有分组,ha+将匹配"ha"、"haa"、"haaa"(仅'a'重复)。
捕获组
组会自动捕获它们匹配的文本,您可以稍后引用:
文本:"John Smith"
正则表达式:(\w+) (\w+)
捕获:组1 = "John",组2 = "Smith"
在大多数编程语言中,您可以访问这些捕获:
// JavaScript示例
const match = "John Smith".match(/(\w+) (\w+)/);
console.log(match[1]); // "John"
console.log(match[2]); // "Smith"
反向引用
反向引用允许您匹配模式中较早的组捕获的相同文本。使用\1、\2等。
正则表达式:(\w+) \1
匹配:重复的单词,如"the the"或"is is"
正则表达式:<([a-z]+)>.*?</\1>
匹配:匹配的HTML标签,如"<div>...</div>"
非捕获组
有时您需要分组而不捕获。使用(?:...)表示非捕获组:
正则表达式:(?:https?|ftp)://\S+
匹配:以http、https或ftp开头的URL
(协议不作为组捕获)
当您不需要引用捕获的文本时,非捕获组可以提高性能。
命名捕获组
命名组使您的正则表达式更具可读性和可维护性:
正则表达式:(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})
匹配:日期,如"2026-03-31"
访问:match.groups.year、match.groups.month、match.groups.day
命名组在复杂模式中特别有用,因为编号引用会变得令人困惑。
快速提示:对于复杂