正则表达式:实用指南与示例
· 12分钟阅读
正则表达式(regex)是开发者工具箱中最强大的工具之一,但它们经常被误解或完全避免使用。无论你是在验证用户输入、解析日志文件,还是转换文本数据,正则表达式都提供了一种简洁高效的方式来匹配字符串中的模式。
这份综合指南将带你从正则表达式基础到高级技巧,提供可以立即在项目中使用的实用示例。最后,你不仅会理解正则表达式如何工作,还会明白何时以及为什么使用它。
目录
什么是正则表达式以及为什么你应该关心?
正则表达式是定义搜索模式的字符序列。可以把它看作是专门为文本中的模式匹配设计的迷你语言。正则表达式不是搜索精确字符串,而是让你描述诸如"任何电子邮件地址"或"此格式的所有电话号码"之类的模式。
正则表达式几乎在每种编程语言和许多命令行工具中都受支持。一旦你学会了语法,就可以在任何地方应用它——从JavaScript和Python到grep和sed。
常见用例包括:
- 验证用户输入(电子邮件、电话号码、密码)
- 从文本中提取数据(解析日志、抓取内容)
- 搜索和替换操作(重构代码、清理数据)
- URL路由和模式匹配
- 语法高亮和词法分析
专业提示: 虽然正则表达式很强大,但它并不总是正确的工具。对于像HTML或JSON这样的复杂解析任务,请使用专用解析器。正则表达式最适合定义明确、相对简单的模式。
正则表达式基础:构建块
每个正则表达式模式都是由基本构建块构成的。在转向更复杂的模式之前,理解这些核心元素至关重要。
字面字符
最简单的正则表达式就是字面文本。模式cat匹配文本中的精确字符串"cat"。大多数字母数字字符直接匹配它们自己。
元字符
某些字符在正则表达式中具有特殊含义。这些被称为元字符,包括:. ^ $ * + ? { } [ ] \ | ( )
要按字面意思匹配这些字符,你需要用反斜杠转义它们。例如,\.匹配字面句点。
点通配符
点.是最基本的通配符——它匹配除换行符外的任何单个字符。模式a.c匹配"abc"、"a1c"、"a-c",但不匹配"ac"(中间没有字符)或"a\nc"(换行符)。
| 模式 | 匹配 | 示例 |
|---|---|---|
. |
任何字符(除换行符外) | a.c匹配abc、a1c、a-c |
\d |
任何数字[0-9] | \d{3}匹配123、456 |
\w |
单词字符[a-zA-Z0-9_] | \w+匹配hello、user_1 |
\s |
空白字符(空格、制表符、换行符) | \s+匹配空格、制表符 |
\D |
非数字 | \D+匹配abc、xyz |
\W |
非单词字符 | \W+匹配!@#、空格 |
\S |
非空白字符 | \S+匹配任何可见文本 |
注意大写版本(\D、\W、\S)是其小写对应项的反义。这是正则表达式语法中的常见模式。
量词:控制重复
量词指定模式应重复多少次。它们放在你想要重复的元素之后,是创建灵活模式的基础。
基本量词
| 量词 | 含义 | 示例 |
|---|---|---|
* |
0次或多次 | ab*c匹配ac、abc、abbc、abbbc |
+ |
1次或多次 | ab+c匹配abc、abbc(不匹配ac) |
? |
0次或1次(可选) | colou?r匹配color、colour |
{n} |
恰好n次 | \d{4}匹配2026、1999 |
{n,} |
n次或更多次 | \d{3,}匹配123、1234、12345 |
{n,m} |
n到m次之间 | \d{2,4}匹配12、123、1234 |
贪婪匹配与懒惰匹配
默认情况下,量词是贪婪的——它们尽可能多地匹配文本。这在匹配HTML标签等模式时可能导致意外结果。
// 贪婪匹配
const text = "<div>Hello</div><div>World</div>";
const greedy = /<.*>/;
// 匹配:"<div>Hello</div><div>World</div>"(整个字符串!)
// 懒惰匹配
const lazy = /<.*?>/;
// 匹配:"<div>"(在第一个闭合括号处停止)
在量词后添加?使其变为懒惰(非贪婪)。懒惰量词在满足模式的同时尽可能少地匹配文本。
快速提示: 在匹配分隔符(引号、括号、标签)之间的内容时,懒惰量词通常是你想要的。使用.*?而不是.*以避免匹配过多。
字符类和快捷方式
字符类让你匹配特定集合中的任何字符。它们使用方括号定义,对于创建灵活的模式非常有用。
基本字符类
// 匹配任何元音
/[aeiou]/
// 匹配任何数字
/[0-9]/
// 匹配任何小写字母
/[a-z]/
// 匹配任何字母(不区分大小写)
/[a-zA-Z]/
// 匹配字母数字字符
/[a-zA-Z0-9]/
否定字符类
在字符类开头使用^来否定它——匹配除了列出的字符之外的任何字符。
// 匹配任何非数字
/[^0-9]/
// 匹配任何非元音
/[^aeiou]/
// 匹配除空格或制表符外的任何字符
/[^ \t]/
字符类中的特殊字符
大多数元字符在字符类内部失去其特殊含义。你可以包含.、*、+和?而无需转义它们。但是,在某些位置你仍然需要转义]、\、^和-。
// 匹配句点或逗号
/[.,]/
// 匹配连字符(转义或放在开头/结尾)
/[-a-z]/或/[a-z-]/
// 匹配闭合括号(必须转义)
/[\]]/
分组和捕获
分组有两个主要目的:它们让你将量词应用于多个字符,并且它们捕获匹配的文本以供以后使用。
捕获组
括号创建记住匹配文本的捕获组。这对于从字符串中提取数据至关重要。
// 提取日期组件
const datePattern = /(\d{4})-(\d{2})-(\d{2})/;
const match = "2026-03-29".match(datePattern);
// match[0]:"2026-03-29"(完整匹配)
// match[1]:"2026"(第一组)
// match[2]:"03"(第二组)
// match[3]:"29"(第三组)
命名捕获组
命名组使你的正则表达式更易读,代码更易维护。你不是通过数字引用组,而是给它们描述性的名称。
// 命名组语法:(?<name>pattern)
const datePattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = "2026-03-29".match(datePattern);
// 通过名称访问
console.log(match.groups.year); // "2026"
console.log(match.groups.month); // "03"
console.log(match.groups.day); // "29"
非捕获组
有时你需要分组用于量词或选择,但不想捕获文本。非捕获组使用(?:...)语法。
// 捕获协议而不创建组
/(?:https?|ftp):\/\/([a-z.]+)/
// 这只创建一个捕获组(域名)
// 协议组(?:https?|ftp)不捕获
非捕获组比捕获组更快,使用更少的内存。当你不需要捕获的文本时使用它们。
反向引用
反向引用让你匹配先前组捕获的相同文本。这对于查找重复单词或匹配成对分隔符很有用。
// 查找重复单词
/\b(\w+)\s+\1\b/
// 匹配"the the"或"hello hello"
// 匹配成对引号
/(['"])(.*?)\1/
// 匹配"hello"或'world'但不匹配"mixed'
选择
管道|运算符创建选择——匹配一个模式或另一个。它就像逻辑OR。
// 匹配cat、dog或bird
/cat|dog|bird/
// 匹配常见文件扩展名
/\.(jpg|jpeg|png|gif|webp)$/i
// 匹配Mr.、Mrs.、Ms.或Dr.
/(?:Mr|Mrs|Ms|Dr)\./
锚点和边界
锚点不匹配字符——它们匹配字符串中的位置。它们对于确保模式出现在特定位置至关重要。
字符串锚点
^匹配字符串的开头(或多行模式下的行)$匹配字符串的结尾(或多行模式下的行)
// 必须以"Hello"开头
/^Hello/
// 必须以"world"结尾
/world$/
// 整个字符串必须恰好是5位数字
/^\d{5}$/
单词边界
\b锚点匹配单词边界——单词字符和非单词字符之间的位置。这对于匹配整个单词至关重要。
// 匹配"cat"作为整个单词
/\bcat\b/
// 匹配:"cat"、"the cat sat"
// 不匹配:"category"、"scat"
// 匹配以"pre"开头的单词
/\bpre\w+/
// 匹配:"preview"、"prepare"、"prefix"
反义\B匹配非单词边界——两侧都是单词字符或两侧都是非单词字符的位置。
专业提示: 在搜索整个单词时始终使用单词边界。没有它们,搜索"cat"也会匹配"category"和"concatenate"。模式\bcat\b确保你只匹配完整的单词。
实际使用的常见模式
这里是经过实战检验的常见验证和提取任务的正则表达式模式。这些模式在简单性和实用准确性之间取得平衡。
| 模式类型 | 正则表达式 | 注释 |
|---|---|---|
| 电子邮件(简单) | ^[\w.-]+@[\w.-]+\.[a-zA-Z]{2,}$ |
适合基本验证 |
| 电子邮件(符合RFC) | ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ |
更严格,广泛接受 |
| URL | ^https?:\/\/[\w.-]+(?:\.[a-zA-Z]{2,})(?:\/\S*)?$ |
基本HTTP/HTTPS URL |
| IPv4地址 | ^(?:\d{1,3}\.){3}\d{1,3}$ |
仅格式,不验证范围 |
| IPv4(严格) | ^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ |
验证0-255范围 |
| 日期(YYYY-MM-DD) | ^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$ |
ISO 8601格式 |
| 时间(24小时) | ^(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?$ |
HH:MM或HH:MM:SS |
| 电话(美国) | ^\+?1?[-.\s]?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$ |
灵活格式 |
| 十六进制颜色 | ^#(?:[0-9a-fA-F]{3}){1,2}$ |
3位或6位十六进制代码 |
| 信用卡 | ^\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}$ |
仅格式,使用Luhn验证 |
| 用户名 | ^[a-zA-Z0-9_-]{3,16}$ |
3-16个字符,字母数字加_和- |
| Slug | ^[a-z0-9]+(?:-[a-z0-9]+)*$ |
URL友好的小写带连字符 |
密码验证
密码验证需要同时检查多个条件。前瞻断言使这成为可能,而无需复杂的逻辑。
// 强密码:8个以上字符,大写、小写、数字、特殊字符
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/
// 分解说明:
// (?=.*[a-z]) - 至少一个小写字母
// (?=.*[A-Z]) -