正则表达式以看起来像随机噪声而闻名。像 ^[\w.-]+@[\w-]+\.\w{2,}$ 这样的模式,看上去就像一只猫在键盘上走过。但在晦涩的语法背后,隐藏着一个非常优雅的理念:一种简洁的语言,用于描述文本中的模式。
无论你是在验证表单输入、搜索日志文件、清洗数据,还是在代码库中进行查找替换,正则表达式都是开发者工具箱中最灵活的工具之一。而且你不需要记住每一个晦涩的特性就能从中获得实际价值。几个核心构件就能覆盖绝大多数实际用例。
正则表达式到底做什么
正则表达式(regex 或 regexp)是一个描述一组字符串的模式。你将它提供给一个搜索引擎——可以是编程语言、文本编辑器或命令行工具中的——它就会找到每个匹配的字符串。
可以把它理解为升级版的搜索查询:
- 普通搜索: 查找精确的单词"error"
- 正则搜索: 查找任何看起来像IP地址、日期、邮箱地址或你能描述的任何结构的内容
正则表达式由数学家Stephen Kleene于1956年发明,并在1960年代通过Unix文本编辑器进入计算领域。如今,几乎所有编程语言和文本编辑器都支持正则表达式。
基本构件
字面字符
最简单的正则表达式就是纯文本。模式 hello 会匹配字符串中任何出现"hello"的地方。就这么简单。
点号(.)——任意字符
点号匹配除换行符以外的任何单个字符。
h.t匹配 "hat"、"hit"、"hot"、"h3t",甚至 "h t"
字符类([])
方括号定义了某个位置允许出现的字符集合。
[aeiou]— 任何元音字母[0-9]— 任何数字[A-Za-z]— 任何字母[^0-9]— 任何非数字字符(方括号内的^表示"非")
简写类
| 简写 | 含义 | 等价形式 |
|---|---|---|
\d |
任意数字 | [0-9] |
\w |
单词字符 | [A-Za-z0-9_] |
\s |
空白字符 | [ \t\n\r] |
\D |
非数字 | [^0-9] |
\W |
非单词字符 | [^A-Za-z0-9_] |
\S |
非空白字符 | [^ \t\n\r] |
量词——匹配次数
量词控制前面的元素必须出现多少次。
| 符号 | 含义 | 示例 | 匹配结果 |
|---|---|---|---|
* |
零次或多次 | ab*c |
"ac"、"abc"、"abbc" |
+ |
一次或多次 | ab+c |
"abc"、"abbc"(不匹配"ac") |
? |
零次或一次 | colou?r |
"color" 和 "colour" |
{3} |
恰好3次 | \d{3} |
"123"、"456" |
{2,4} |
2到4次 | \d{2,4} |
"12"、"123"、"1234" |
锚点——位置
^— 字符串开头(使用m标志时为行首)$— 字符串结尾(或行尾)\b— 单词边界
模式 ^\d{4}$ 匹配恰好由四个数字组成的字符串,如"2026",但不匹配"abc2026"或"2026xyz"。
分组和选择
(abc)— 将"abc"捕获为一个组a|b— 匹配"a"或"b"(cat|dog)— 匹配"cat"或"dog"
分组还允许你对序列应用量词:(ha)+ 匹配"ha"、"haha"、"hahaha"。
常见实用模式
验证邮箱(基础版)
^[\w.-]+@[\w-]+\.\w{2,}$
它可以匹配 user@example.com、first.last@company.co.uk(部分匹配),并拒绝没有 @ 或域名的字符串。
重要提示: 用正则表达式完美验证邮箱地址是出了名的困难——完整的RFC 5322规范极其复杂。对于生产系统,建议使用基础正则进行格式检查,然后通过发送确认邮件来验证地址。
匹配电话号码
\+?\d{1,3}[-.\s]?\(?\d{1,4}\)?[-.\s]?\d{3,4}[-.\s]?\d{3,4}
它可以处理 +1 555 123 4567、555-123-4567 和 (555) 123-4567 等格式。
匹配URL
https?://[\w.-]+(/[\w./?&=-]*)?
匹配 https://example.com、http://example.com/path?q=hello。
匹配日期(YYYY-MM-DD)
\d{4}-\d{2}-\d{2}
匹配 2026-03-29、1999-12-31。注意:这只检查格式,不检查有效性——它也会匹配 9999-99-99。
改变行为的标志
大多数正则引擎支持修改模式应用方式的标志:
| 标志 | 名称 | 效果 |
|---|---|---|
g |
全局 | 查找所有匹配项,而不仅是第一个 |
i |
不区分大小写 | hello 匹配 "Hello"、"HELLO" 等 |
m |
多行 | ^ 和 $ 匹配每行的开头/结尾 |
s |
点号全匹配 | . 也匹配换行符 |
在JavaScript中,标志附加在结尾斜杠之后:/hello/gi。在Python中,作为参数传递:re.findall(r"hello", text, re.IGNORECASE)。
正则表达式何时是多余的
正则表达式虽然强大,但并不总是最佳工具:
- 解析HTML或XML。 请使用合适的DOM解析器。正则无法可靠地处理嵌套标签。
- 解析JSON。 请使用
JSON.parse()或等效方法。正则在边界情况下会出错。 - 复杂验证。 如果你的模式跨越多行且需要五分钟才能读懂,考虑改用过程式验证代码。
- 简单字符串操作。 如果你只需要
startsWith()、includes()或split(),普通字符串方法更清晰也更快。
常见陷阱
- 忘记转义特殊字符。 点号
.匹配任何字符。要匹配字面点号,请使用\.。(、)、[、]、+、*、?、{、}、^、$、|和\同理。 - 贪婪匹配与惰性匹配。 默认情况下,
.*是贪婪的——它会匹配尽可能多的内容。添加?使其变为惰性:.*?匹配尽可能少的内容。在提取分隔符之间的内容时,这一点很重要。 - 灾难性回溯。 嵌套量词如
(a+)+在某些输入上可能导致引擎尝试指数级数量的路径,使你的程序冻结。避免在重叠模式上使用嵌套重复。 - 忘记锚点。 没有
^和$,你的模式会匹配子字符串。\d{3}会在 "abc12345" 中匹配 "123"。如果需要精确匹配,请使用^\d{3}$。
进阶学习
学习正则表达式最好的方式是实验。输入一个模式,粘贴一些测试文本,看看哪些内容被高亮。不断调整和迭代,直到你理解每个部分的工作原理。
- 如何测试正则表达式模式 — 包含示例的交互式教程
- 正则表达式测试器 — 粘贴你的模式和测试数据,实时查看匹配结果高亮显示
