正则表达式指南
人生苦短,我用正则。
你有没有过这种体验:看着一串奇形怪状的符号,心里直犯嘀咕——这是在写魔法咒语吗?别怕,那不是巫术,那是正则表达式!虽然它一开始看起来像外星语言,但一旦掌握,你就能轻松驾驭文本世界,像武林高手挥剑断水一样精准又帅气。
今天,让我们带着轻松的心情,一起拆解正则表达式这位“复杂又迷人的老朋友”,顺便吐槽它那些让人又爱又恨的小脾气。
什么是正则表达式?
正则表达式(Regular Expression,简称 RegEx),说白了就是一套用来描述文本模式的神奇符号规则。它能帮你快速找到、匹配、替换文本中的特定内容,效率堪比 Ctrl+F,但姿势更优雅。
想象一下,正则就像一位脾气古怪但超强的猎犬,只要你给出线索(规则),它就能飞速在大海一样的数据里嗅出目标,叼回来给你。
一个小小的栗子 🌰
我们先来看一个最简单的例子,感受一下正则的神奇魅力:
假设你想在一大堆文字中找到所有的数字。正常人:👀肉眼一个个找;程序员:💻正则一把梭!
只需要一条简简单单的正则:
1 | \d |
解释一下:
\d代表 任意一个数字,也就是 0 到 9 中的任意一个。- 它只匹配一个数字,如果想匹配一串数字,比如身份证号、电话号?加个
+就行了(后面会讲~)。
比如,在下面这段话里:
今天是 2025 年 4 月 27 日,我喝了 3 杯咖啡。
用正则 \d,就能快速找到:2、0、2、5、4、2、7、3 —— 简直像开了挂一样!
接下来,我们就要逐步揭秘正则表达式里的更多神器——各种特殊符号和操作符,让你也能用正则秀出风采!🎩✨
基本匹配:一点也不复杂
在正则表达式的世界里,最基础的事情就是:你写啥,它就找啥。
比如:
- 正则
cat,能在 “I have a cat.” 里找到 cat。 - 正则
dog,能在 “Hotdog is delicious.” 里找到 dog。
很简单吧?直接对着文字比对,一模一样才能算数。
.:万物皆可匹配
. 在正则里,代表“任意单个字符”,除了换行符(\n)之外,其他的通通匹配。
比如:
- 正则
c.t,可以匹配cat、cut、cot,甚至c9t,只要中间那个字符存在就行。
. 是个小疯子,什么都要,什么都敢要,记得小心使用!
字符集:你可以自己挑
如果你只想匹配几个特定的字符,可以用方括号 [ ] 包起来,组成一个“字符小团体”。
比如:
[abc]匹配一个字符,可以是a、b或c。
举例:
- 正则
c[aeiou]t,可以匹配cat、cot、cut、cet、cit,因为中间只允许出现元音字母。
字符集 = 自助餐,随便挑一个吃,不挑别的。
否定字符集:我啥都不要,只要排除他们
在字符集里加个 ^(插在最前面),就变成了否定字符集,意思是“匹配不在这里面的”。
比如:
[^0-9],匹配任何不是数字的字符。[^aeiou],匹配任何不是元音字母的字符。
注意:^ 只有在方括号 [ 后面立刻出现时,才表示否定!放在外面那是另一个意思了!(后面讲)
字母范围 & 数字范围:别傻傻一个个列
如果你想匹配从 a 到 z 所有小写字母,或者从 0 到 9 所有数字,可以用范围表达,省事儿。
格式就是用一个短横线 - 连起来:
[a-z]:匹配任意小写字母。[A-Z]:匹配任意大写字母。[0-9]:匹配任意数字。
当然你也可以组合:
[a-zA-Z0-9]:匹配所有字母和数字,走到哪儿都好用的万金油组合。
重复匹配:*、?、+、{}的华丽登场
匹配一个字符?太单调了!
匹配很多个字符?这才叫快乐!
正则提供了一套“量词”,让你可以控制“匹配多少次”。
*:0次或者多次
* 表示前面的东西可以出现 零次、一次或者多次,想来多少来多少。
比如:
- 正则
go*gle可以匹配:- `ggle`(一个 `o` 都没有) - `gogle` - `gooogle` - `goooooooogle`
* 是一位极度包容的老好人,不来可以,来一大堆也没问题。
?:要来就来一个,不来也行
? 表示前面的东西可以出现 零次或一次,要么有,要么没有,别太贪心。
比如:
- 正则
colou?r可以匹配:colorcolour
英式拼法美式拼法?一个 u 搞定!
+:至少来一个,别想偷懒
+ 表示前面的东西必须出现 至少一次,一次以上没上限。
比如:
- 正则
lo+l可以匹配:lollooollooooooooool
但不能匹配只有 ll 的情况,因为中间必须有至少一个 o。
{}:精准打击选手
如果你想精确地控制出现的次数,可以用花括号 {}:
{n}:正好 n 次{n,}:至少 n 次{n,m}:至少 n 次,最多 m 次
比如:
\d{4}匹配恰好四位数字,比如年份2025。\d{2,4}匹配2到4位数字,比如23、202、2025。
这种写法就像开精准制导导弹,目标明确,打击到位!
🎯 小结:
*:可以啥都没有,或者来一堆。?:可有可无,没啥强制要求。+:必须有一个起步。{}:想来几次,咱自己说了算!
分组玩法:组织一下,世界更美好
正则表达式虽然自由,但自由久了容易乱套。所以,分组 () 机制就诞生了:把一段东西抱在一起,作为一个整体来处理。
就像开会拉个小组讨论,大家更有组织感!
():普通分组
普通的圆括号 () 有两个作用:
- 把内容打包成一个整体。比如你想重复一整块内容,就可以打包起来再用
*、+等量词。 - 捕获匹配的内容。也就是可以把匹配到的小块内容拿出来以后用。
比如:
- 正则
(ab)+可以匹配:abababababab
每次匹配的是整个 ab,而不是单独的 a 或 b。
捕获的内容也可以在替换文本时用,比如 \1 代表第一个括号里的内容,超实用!(后面举例)
(?:):非捕获分组
有时候你只是想分个组,方便管理,但你压根不想“捕获”它。
这时就用 (?:...) —— 非捕获分组!
比如:
- 正则
(?:cat|dog)+会匹配:catdogcatdogcat
但不会单独记录是哪个子串,纯粹为了逻辑清晰,不留证据,不搞记录。
总结:
():分组 + 捕获(?:):分组,不捕获,低调做人。
其他好用的家伙们:|、转义符、^、$
到了这里,正则的武器库里还剩下一些必学技能,咱们逐个拿出来摆一摆!
|:二选一?多选一!
竖线 | 表示或者(OR)。
比如:
- 正则
cat|dog可以匹配cat或dog,有点像语文里的“选择题”。
配合括号使用效果更佳,比如:
(cat|dog|fish),这回是猫猫狗狗加小鱼干,三选一!
转义字符:想用特殊符号?加个 \
有些符号在正则里本来是有特殊意义的,比如 ., *, ?, +, (, ), [, ], {, }, |, ^, $,它们都有各自的小性格。
如果你就是想字面上匹配这些符号,就要在前面加个 \,转义它!
比如:
\.匹配真正的句点.\*匹配真正的星号*\\匹配一个反斜杠\(因为反斜杠自己也需要转义,懂得都懂)
总之:遇到叛逆符号,加个 \ 驯服它!
^:开头对线
^ 有两种用法(别跟字符集里的 [^ ] 搞混!)
在正则开头,^ 表示字符串的开始。
比如:
- 正则
^Hello只会匹配以Hello开头的字符串,比如:- ✅
Hello world! - ❌
Say Hello!(因为Hello不在开头)
- ✅
小提醒:^ 是个很讲原则的人,只在最开头严格守岗。
$:结尾封印
$ 表示字符串的结束。
比如:
- 正则
world!$只会匹配以world!结尾的字符串,比如:- ✅
Hello world! - ❌
Welcome to the world! Hello(虽然有world!,但不是结尾)
- ✅
如果你又用了 ^ 又用了 $,比如:
^abc$
就要求整个字符串只能是abc,中间不允许有多余的字符,超严格!
🎯 小结:
|:你选我或者选我或者选我。\:乖,别皮,给你加个转义。^:我只看开头。$:我只守结尾。
常见特殊字符:懒人速记宝典
如果你觉得 [a-zA-Z0-9_] 写起来太麻烦,别急,正则贴心地给了你一些懒人专用的速记字符,直接一招搞定!
\w 和 \W:字母数字下划线 vs 非字母数字下划线
\w:匹配字母、数字和下划线(等价于[a-zA-Z0-9_])\W:匹配不是字母、数字、下划线的字符(就是\w的反面)
比如:
- 正则
\w+可以匹配单词、用户名、变量名,比如hello_123。 - 正则
\W可以匹配空格、标点符号、表情包(?)这种奇奇怪怪的玩意儿。
\s 和 \S:空白字符 vs 非空白字符
\s:匹配所有空白字符(空格、Tab、换行、回车……)\S:匹配所有非空白字符
比如:
- 正则
\s+可以用来找出一大堆空格,比如排版时顺手清理掉那些乱飞的空格怪。 - 正则
\S+可以快速找到连续的非空白内容,比如提取一整段话。
\d 和 \D:数字 vs 非数字
\d:匹配数字(0到9)\D:匹配非数字的任何东西
比如:
- 正则
\d{3}-\d{4}可以匹配一个形如123-4567的电话号码后缀。 - 正则
\D+则可以找到连绵不断的字母、符号、奇奇怪怪的文本串。
🎯 小结:
| 速记符号 | 匹配的内容 |
| :———: | :———————————: |
| \w | 字母、数字、下划线 |
| \W | 非字母、非数字、非下划线 |
| \s | 空白字符 |
| \S | 非空白字符 |
| \d | 数字 |
| \D | 非数字 |
零宽断言:窥视未来,不留下脚印
来到高阶正则技巧——零宽断言!(又称“零宽度匹配”)
它们神奇地做到:检查某个条件是否成立,但不真正消费任何字符。就像一个隐身侦察兵,观察后留下一片寂静。
主要有四种:
正向先行断言(Positive Lookahead)
格式:X(?=Y)
意思是:匹配 X,只有在它后面紧跟着 Y 时才算成功,但是匹配结果只包含X,不包括Y。
比如:
- 正则
\d+(?=元)
可以匹配100(在字符串 “100元” 中),但只提取100,不会把元算进去。
简单理解:我要X,但我得偷偷瞄一眼它后面是不是Y。
负向先行断言(Negative Lookahead)
格式:X(?!Y)
意思是:匹配 X,只有在它后面 不是Y 的情况下才算成功。
比如:
- 正则
foo(?!bar)
可以匹配foo,但只在后面不是bar的情况下。
简单理解:我要X,但后面不能跟着那个讨厌的Y。
正向后行断言(Positive Lookbehind)
格式:(?<=Y)X
意思是:匹配 X,前面必须是Y,但只捕获X。
比如:
- 正则
(?<=\$)\d+
可以匹配 “$100” 中的100,只要前面有个$符号就行。
简单理解:只有在Y后面出生的X,我才要。
负向后行断言(Negative Lookbehind)
格式:(?<!Y)X
意思是:匹配 X,前面不能是Y。
比如:
- 正则
(?<!\$)\d+
可以匹配没有$符号标记的钱数,比如找出纯数字的价格。
简单理解:只要前面不是Y,X就自由飞翔!
🎯 小结版对比表:
| 名称 | 方向 | 要求 | 例子(匹配部分) |
|---|---|---|---|
| 正向先行断言 | 后看 | 必须有Y | \d+(?=元) |
| 负向先行断言 | 后看 | 不能有Y | foo(?!bar) |
| 正向后行断言 | 前看 | 必须有Y | (?<=\$)\d+ |
| 负向后行断言 | 前看 | 不能有Y | (?<!\$)\d+ |
小结一句话总结:
零宽断言,就是悄悄打量,不打扰,验身份,不占地方。
正则标志:戴上不同的眼镜看世界
写正则时,我们可以加一些标志(Flags),就像给正则戴上一副魔法眼镜,立刻改变它的行为方式!
这些标志通常写在正则外面,比如 /pattern/flags,不同语言的写法略有差异,但灵魂是一致的!
g:全局匹配(global)
默认情况下,正则只会找到第一个匹配就停下(就像只抓到一条鱼就不钓了)。
加上 g 标志,正则就会一路狂奔,找到所有符合的匹配!
比如:
- 正则
/cat/g在字符串"cat dog cat"中会匹配两次cat。
简单理解:带上 g,战斗到最后一兵一卒!
i:忽略大小写(ignore case)
有些时候,大小写不重要,做人要宽容,匹配也一样。
加上 i 标志,正则就不区分大小写了!
比如:
- 正则
/hello/i可以匹配Hello、HELLO、hElLo……
简单理解:我不在乎你是大写小写,心灵契合才是王道。
m:多行模式(multiline)
默认情况下,^ 和 $ 只匹配整串文本的开头和结尾。
加上 m 标志,^ 匹配每一行的开头,$ 匹配每一行的结尾!
比如:
文本是:helloworld
正则
/^world/m可以匹配到world,因为现在每一行都有自己的“起点”了。
简单理解:每一行,都值得被认真对待!
🎯 小结:
| 标志 | 作用 |
|---|---|
g |
全局匹配,找到所有匹配 |
i |
忽略大小写匹配 |
m |
多行模式,行行高亮 |
小提醒:
有些语言(比如 Python 的 re 模块)用 re.IGNORECASE、re.MULTILINE 这样写,但道理是一样的!
贪婪与非贪婪匹配:吃多少,够就好
正则界的量词(比如 *、+、{})默认是贪婪的,也就是说:
能吃多少,就吃多少!
但有时候,我们想要它矜持一点,只吃够的那一口,不要狼吞虎咽。
这时候,就要使用非贪婪匹配(也叫懒惰匹配,Lazy)。
贪婪匹配:能吃绝不剩!
默认的贪婪量词:
*:匹配 0 次或多次(尽可能多)+:匹配 1 次或多次(尽可能多){n,m}:匹配 n 到 m 次(尽可能多)
比如:
- 正则
<.*>匹配"hello <b>world</b> text"会得到<b>world</b>,而不是<b>。
因为.*太贪婪了,一口气吃到了最后一个>!
非贪婪匹配:适可而止
在量词后面加一个 ?,就变成了非贪婪量词!
*?:匹配 0 次或多次(尽可能少)+?:匹配 1 次或多次(尽可能少){n,m}?:匹配 n 到 m 次(尽可能少)
比如:
- 正则
<.*?>匹配"hello <b>world</b> text"会得到<b>,
因为.*?只吃到遇到第一个>为止,超有分寸!
🎯 贪婪 vs 非贪婪 简明对比:
| 匹配方式 | 表达式 | 行为 |
|---|---|---|
| 贪婪匹配 | <.*> |
尽可能多,吃到最后一个 > |
| 非贪婪匹配 | <.*?> |
尽可能少,遇到第一个 > 就收手 |
小总结一句话:
贪婪匹配:全世界我都要!
非贪婪匹配:遇到就好,不强求~
写在最后:正则之路,刚刚开始!
恭喜你一路披荆斩棘,走到这里!
如果你认真看完了这篇正则表达式指南,那么恭喜你,已经比绝大多数程序员更懂正则了!(没开玩笑)
当然,正则表达式就像一门武功,只看不练,是学不会六脉神剑的。
真正的掌握,需要多敲、多练、多踩坑、多吐槽。
为了让你快速升级打怪,我特别推荐一个超棒的网站:
这个网站简直就是正则界的小甜品店!
- 每一课短小精悍,轻松易懂
- 支持中文界面,不用翻词典
- 在线就能练习,做题刷经验
- 还能直接看到匹配效果,快乐加倍!
最后,送你一句话作为结尾:
正则表达式不是魔法,它只是复杂得像魔法而已。
慢慢来,哪怕今天只学会了一个.,也已经比昨天更强啦!
(完)
