前言
正则表达式(Regular Expression,简称 Regex)是程序员手中最强大的文本处理工具之一。它像是一把精密的“手术刀”,能从杂乱无章的字符串中精准地提取、替换或验证数据。
然而,正则的语法往往晦涩难懂。本文将结合关于“贪婪模式”和“捕获组”的疑问,由浅入深地拆解正则的核心逻辑。
一、 核心概念:正则到底是什么?
简单来说,正则表达式就是一种描述字符串特征的语言。
- 普通字符串:
"abc"—— 只能匹配 "abc" 这三个字母。 - 正则表达式:
\d{3}-\w+—— 匹配“3个数字”、“一个横杠”、“任意多个单词字符”。
💡 Tip:写正则时,不要想着“我要找哪个词”,而要想着“我要找的这个词长什么样”。
二、 基础语法:构建匹配的积木
1. 元字符(Metacharacters)
元字符是正则中的特殊符号,它们不代表字面意思,而是代表一类字符。
| 符号 | 含义 | 示例 | 匹配内容 |
|---|---|---|---|
. | 任意字符(除换行符) | a.c | abc, a1c, a c |
\d | 数字 (digit) | \d\d | 12, 99 |
\w | 单词字符 (word) | \w+ | hello, user_1 |
\s | 空白字符 (space) | a\sb | a b, a↹b |
^ | 开头 | ^Hello | Hello world (首词) |
$ | 结尾 | end$ | The end (尾词) |
2. 量词(Quantifiers):控制出现的次数
量词决定了前面的字符要出现多少次。
| 符号 | 含义 | 示例 |
|---|---|---|
* | 0次或多次 | ab*c → ac, abc, abbc |
+ | 1次或多次 | ab+c → abc, abbc (不匹配 ac) |
? | 0次或1次 | ab?c → ac, abc |
{n} | 恰好 n 次 | \d{3} → 123 |
{n,m} | n 到 m 次 | \d{2,4} → 12, 123, 1234 |
三、 进阶核心:贪婪与非贪婪(痛点)
这是正则中最容易踩坑的地方之一。
1. 贪婪模式
默认行为。正则引擎会尽可能多地匹配字符,“吃掉”所有内容,如果不满足后续条件再吐出来(回溯)。
- 写法:
.*,.+,\d+ - 场景:匹配整行内容,或者确定没有嵌套结构时。
2. 非贪婪/懒惰模式
加上 ? 。正则引擎会尽可能少地匹配字符,一旦满足后续条件立即停止。
- 写法:
.*?,.+?,\d+? - 场景:HTML标签提取、成对符号之间的内容提取。
实战对比
假设文本为:<div>标题1</div><div>标题2</div>
-
贪婪
<div>.*</div>:- 结果:
<div>标题1</div><div>标题2</div> - 原因:第一个
.*一口气吞到了最后一个</div>。
- 结果:
-
非贪婪
<div>.*?</div>:- 结果:
<div>标题1</div> - 原因:
.*?遇到第一个</div>就停下了。
- 结果:
四、 数据提取:分组与捕获
匹配只是第一步,把数据“扣”出来才是目的。
1. 捕获组 ()
括号不仅改变优先级,还会将匹配到的内容保存下来。
const str = "2023-10-05";
const reg = /(\d{4})-(\d{2})-(\d{2})/;
// $1 = 2023, $2 = 10, $3 = 05
2. 非捕获组 (?:)
如果你只需要括号来改变优先级(比如配合 |),但不需要保存内容,使用 ?: 可以节省内存并提高速度。
// 匹配 http 或 https,但不需要单独提取协议头
/(?:https?)://www/
3. 命名捕获组 (?<name>)
现代语言(JS ES2018+, Python, Java)支持给组起名字,代码可读性极大提升。
const reg = /(?<year>\d{4})-(?<month>\d{2})/;
// 结果.groups.year -> "2023"
五、 高级技巧:断言(Assertions)
断言只判断位置,不消耗字符(不占宽度)。
| 名称 | 符号 | 含义 | 示例 |
|---|---|---|---|
| 正向先行 | (?=...) | 后面必须是... | Windows(?=10) 匹配 Windows 10 中的 Windows |
| 负向先行 | (?!...) | 后面不能是... | Windows(?!10) 匹配 Windows 7 中的 Windows |
| 正向后行 | (?<=...) | 前面必须是... | (?<=$)\d+ 匹配 $100 中的 100 |
| 负向后行 | (?<!...) | 前面不能是... | (?<!$)\d+ 匹配不带$的数字 |
六、 避坑指南与最佳实践
- HTML解析慎用正则:虽然正则能处理简单的 HTML,但面对复杂的嵌套、属性乱序时极其脆弱。生产环境建议使用 DOM 解析器(如 Cheerio, BeautifulSoup)。
- 性能陷阱:避免在量词中嵌套量词,如
(a+)+,这会导致灾难性回溯,让程序卡死。 - 开启多行模式:在处理多行文本块时,记得加
m标志(让^和$匹配每一行的首尾)或s标志(让.匹配换行符)。 - 善用工具:不要盲写。使用 Regex101 或 VS Code 的正则搜索功能实时调试。
七、 总结
掌握正则的三个阶段:
- 入门:理解
\d,\w,.和基本量词。 - 熟练:深刻理解贪婪与非贪婪的区别,熟练使用捕获组提取数据。
- 精通:掌握断言处理复杂逻辑,了解引擎回溯原理以优化性能。
正则表达式是一门“写时痛苦,用时爽快”的技能。希望这篇文章能帮你初步认识正则表达式!