正则表达式是用来描述和匹配文本模式的工具。通过学习字符类、量词、锚点、分组与环视等概念,并结合常见实例(邮箱、手机号、URL、提取替换等),你可以用一套可移植的思路在多种语言和工具中高效处理文本。本文以通俗、一步步带你上手的方式讲解要点、常见陷阱和实战片段,边写边想,带点真实的注解和可立即复用的正则片段。

为什么要学正则表达式
简单说,正则表达式能把“复杂的文本处理变成模式匹配”的工作。你会发现很多重复、琐碎或容易出错的文本任务——比如日志筛选、表单校验、批量替换——用正则都能写得简洁而可靠。当然,学会正则不仅是记住符号,更重要的是学会用“模式思维”去拆解问题。
先把基本概念弄清楚
先像学一门语言那样把基础词汇记住,然后通过例子把它们串起来。
字符与字面量
最直观的就是字面量匹配:abc 就是匹配字符序列 abc。若要匹配元字符(比如点号、星号),需用反斜杠转义:\.、\*。
常用元字符一览
- .:匹配除换行外的任意单个字符(取决于模式标志)
- ^ 和 $:行/字符串的起始与结束锚点
- []:字符类,像 [aeiou] 表示一个元音字符
- [^]:否定字符类,例如 [^0-9]
- |:或,像 cat|dog
- ():分组与捕获
- \:转义符,用来引用特殊含义或常用字符类(\d、\w、\s 等)
量词:控制重复
量词决定了前面元素出现的次数。
| 符号 | 含义 | 例子 |
| * | 0 次或多次(贪婪) | ab*c 匹配 ac、abc、abbbc |
| + | 1 次或多次 | ab+c 匹配 abc、abbbc,但不匹配 ac |
| ? | 0 次或 1 次 | colou?r 匹配 color 和 colour |
| {n,m} | 至少 n 次,至多 m 次 | \d{2,4} 匹配 2 到 4 位数字 |
量词通常是贪婪的(尽量多匹配)。如果想要最小匹配,很多引擎支持懒惰量词,比如 .*?。
位置锚点与边界
锚点帮助你限定匹配位置:
- ^:匹配字符串/行的开始(取决于多行标志 m)
- $:匹配字符串/行的结束
- \b:单词边界(字母/数字与非字母/数字之间)
- \B:非单词边界
这样你可以做更精确的校验,比如以数字开头并以分号结尾的行。
分组、捕获与引用
分组是正则强大的地方。括号不仅把子模式组织在一起,还可以捕获文本以供后续引用或替换。
- 捕获组:(…)。在替换中常用 $1(JS/许多工具)或 \1(一些工具/引擎)引用第一个捕获组。
- 非捕获组:(?:…),用于组合而不占用捕获编号。
- 命名捕获(不同引擎语法不同):常见如 (?P
…) (Python)或 (?…) (PCRE/JS 的较新实现)。
回溯引用允许你匹配重复的子串,例如要找到成对的重复单词:\b(\w+)\s+\1\b(大多数引擎中可用)。
环视(Lookaround)——在匹配的同时看左边或右边
环视不会消耗字符,只是对位置进行条件判断。分为前瞻和后顾、正和负两类:
- 正前瞻:foo(?=bar),匹配 foo,条件是后面紧跟 bar
- 负前瞻:foo(?!bar),匹配 foo,条件是后面不是 bar
- 正后顾:(?<=bar)foo,匹配 foo,条件是前面是 bar
- 负后顾:(?
注意:并非所有引擎都支持变长后顾(如 JS 直到某些版本不支持),所以写跨平台正则时要小心。
实用示例(带思路,不只是正则)
1)邮箱地址(实用且保守的版本)
完全符合 RFC 的邮箱正则会非常复杂,而且在大多数场景不必要。一个平衡可用的常见写法:
^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$
说明:本模式校验常见邮箱格式,限制顶级域名至少 2 个字母。如果你要更严格(比如 IDN、特殊域名),需额外处理或用专业库。
2)手机号(中国大陆常见 11 位)
^1[3-9]\d{9}$
思路:以 1 开头,第二位是 3-9 的某个数字,后续 9 位数字。根据运营商段或国际号码会变,注意不要把校验规则写死在所有场景里。
3)URL(简化版)
URL 有很多变种,下面是一个常见简化版:
^(https?:\/\/)?([A-Za-z0-9-]+\.)+[A-Za-z]{2,}(:\d+)?(\/\S*)?$
说明:可选协议、域名、可选端口和路径。不要用一个正则去做所有 URL 的“真实检查”,更好的方式是用专门的 URL 解析库完成语义检查。
4)提取 HTML 标签内的内容(谨慎使用)
用正则处理复杂或嵌套的 HTML 通常是不可靠的,但针对单一简单标签可以:<([a-z]+)(?:\s[^>]*)?>(.*?)<\/\1>。这能匹配基本的成对标签并捕获内容与标签名。
跨语言与常见工具差异
不同环境对正则的支持、语法或默认行为有差异,几个要点:
- JavaScript:早期不支持可变长后顾,命名组较新版本才支持,替换字符串中用 $1。
- Python(re):支持 (?P
…) 命名组,替换中用 \\1 或 \g。另外有 regex 第三方模块增加了更强功能。 - Java:使用双反斜杠转义(字符串字面量中),命名组语法类似于 PCRE 的形式但有差异。
- 命令行工具(grep/sed/awk):语法各异,基本 POSIX 或 PCRE 带来差别,工具间替换语法也不一样(如 sed 用 \1)。
性能与调优小贴士
正则既强大也可能惹性能问题。几个常见经验:
- 尽量用具体字符而非 .* 盲扫;在能锚定位置时先锚定(用 ^ 或明确前缀)
- 避免回溯爆炸(catastrophic backtracking):当有重叠的模糊量词组合(如 (a+)+ 或 (.*a){n})时容易产生极端慢速
- 使用非回溯原子项(某些引擎支持)或占有量词(possessive quantifiers,如 PCRE 的 +、*+)降低回溯
- 在循环或高频场景预编译正则(许多语言支持预编译/缓存模式)
常见错误与调试技巧
我自己常犯的错误也分享下,免得你踩坑:
- 忘记对斜杠和反斜杠双重转义(尤其在字符串字面量里)
- 误用贪婪量词导致匹配过多(把 .* 改为 .*? 或更精确的字符集)
- 混淆捕获组编号和命名组(不同引擎替换语法不同)
- 在多行文本里忘记设置多行标志或 DOTALL(让 . 匹配换行)
调试工具很有用:把你的正则粘到像 regex101、regexr(工具名)这样的在线测试器上,可以看到分组、渐进匹配和回溯信息(记得不要把隐私数据直接粘到在线工具)。
替换与重排:实战小技巧
替换时要注意目标引擎的引用语法:
- JavaScript、许多在线工具:替换使用 $1、$2 等
- 一些工具或语言(如传统 sed/awk):使用 \1 之类
例如,把名字格式从 “姓, 名” 变成 “名 姓”:模式 ^([^,]+),\s*(.+)$,替换用 $2 $1(在 JS 中)。
进阶话题简要触及(以便日后深入)
- Unicode 与脚本问题:如果处理多语言文本,使用支持 Unicode 类别的引擎(\p{L} 等)会更稳妥。
- 递归与条件模式:PCRE 等高级引擎支持递归匹配(用于嵌套结构),但跨平台可移植性差。
- 状态机与实现差异:一些工具用 NFA(回溯)实现,一些使用 DFA(线性时间),这影响性能特性。
一些常用正则片段(便于复制粘贴并理解)
- 去除字符串首尾空白(大多数语言 trim 用库好,不用正则,但示例):^\s+|\s+$(替换为空串)
- 只保留数字:\D+(替换为空串)
- 匹配 0-255 范围的 IPv4 四段之一(片段):(25[0-5]|2[0-4]\d|1?\d{1,2})
- 匹配 ISO 日期(YYYY-MM-DD)基本形式:^\d{4}-\d{2}-\d{2}$(注意没有语义校验闰年等)
如何系统练习与掌握
学习正则不要只背公式。建议的路径:
- 先记住基础元字符和简单量词,用真实例子练手(日志、CSV、文本报告)
- 每次遇到任务,先把目标写成“我想从文本中提取什么”一句话,再拆成小子任务
- 用测试工具逐步构造正则,观察每一步的匹配结果和分组内容
- 把复杂任务拆成多个小正则或先用解析器/库完成复杂语义,再用正则做局部清理
写到这里我自己又回想起好多次因为懒用一个 .* 把整个日志吃掉的尴尬瞬间,倒也是好的提醒:学会正则最实用的收益不是记住一个神奇的模式,而是学会用最小的、最清晰的模式去解决问题。接下来你可以挑一两项上面的示例在你的代码/工具里试一遍,边试边改就会更快上手。