人生苦短,我用正则。

你有没有过这种体验:看着一串奇形怪状的符号,心里直犯嘀咕——这是在写魔法咒语吗?别怕,那不是巫术,那是正则表达式!虽然它一开始看起来像外星语言,但一旦掌握,你就能轻松驾驭文本世界,像武林高手挥剑断水一样精准又帅气。

今天,让我们带着轻松的心情,一起拆解正则表达式这位“复杂又迷人的老朋友”,顺便吐槽它那些让人又爱又恨的小脾气。

什么是正则表达式?

正则表达式(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,可以匹配 catcutcot,甚至 c9t,只要中间那个字符存在就行。

. 是个小疯子,什么都要,什么都敢要,记得小心使用!


字符集:你可以自己挑

如果你只想匹配几个特定的字符,可以用方括号 [ ] 包起来,组成一个“字符小团体”。

比如:

  • [abc] 匹配一个字符,可以是 abc

举例:

  • 正则 c[aeiou]t,可以匹配 catcotcutcetcit,因为中间只允许出现元音字母。

字符集 = 自助餐,随便挑一个吃,不挑别的。


否定字符集:我啥都不要,只要排除他们

在字符集里加个 ^(插在最前面),就变成了否定字符集,意思是“匹配不在这里面的”。

比如:

  • [^0-9],匹配任何不是数字的字符。
  • [^aeiou],匹配任何不是元音字母的字符。

注意:^ 只有在方括号 [ 后面立刻出现时,才表示否定!放在外面那是另一个意思了!(后面讲)


字母范围 & 数字范围:别傻傻一个个列

如果你想匹配从 az 所有小写字母,或者从 09 所有数字,可以用范围表达,省事儿。

格式就是用一个短横线 - 连起来:

  • [a-z]:匹配任意小写字母。
  • [A-Z]:匹配任意大写字母。
  • [0-9]:匹配任意数字。

当然你也可以组合:

  • [a-zA-Z0-9]:匹配所有字母和数字,走到哪儿都好用的万金油组合。

重复匹配:*、?、+、{}的华丽登场

匹配一个字符?太单调了!
匹配很多个字符?这才叫快乐!

正则提供了一套“量词”,让你可以控制“匹配多少次”。


*:0次或者多次

* 表示前面的东西可以出现 零次、一次或者多次,想来多少来多少。

比如:

  • 正则 go*gle 可以匹配:
      - `ggle`(一个 `o` 都没有)
      - `gogle`
      - `gooogle`
      - `goooooooogle`
    

* 是一位极度包容的老好人,不来可以,来一大堆也没问题。


?:要来就来一个,不来也行

? 表示前面的东西可以出现 零次或一次,要么有,要么没有,别太贪心。

比如:

  • 正则 colou?r 可以匹配:
    • color
    • colour

英式拼法美式拼法?一个 u 搞定!


+:至少来一个,别想偷懒

+ 表示前面的东西必须出现 至少一次,一次以上没上限。

比如:

  • 正则 lo+l 可以匹配:
    • lol
    • loool
    • looooooooool

但不能匹配只有 ll 的情况,因为中间必须有至少一个 o


{}:精准打击选手

如果你想精确地控制出现的次数,可以用花括号 {}

  • {n}:正好 n 次
  • {n,}:至少 n 次
  • {n,m}:至少 n 次,最多 m 次

比如:

  • \d{4} 匹配恰好四位数字,比如年份 2025
  • \d{2,4} 匹配2到4位数字,比如 232022025

这种写法就像开精准制导导弹,目标明确,打击到位!


🎯 小结:

  • *:可以啥都没有,或者来一堆。
  • ?:可有可无,没啥强制要求。
  • +:必须有一个起步。
  • {}:想来几次,咱自己说了算!

分组玩法:组织一下,世界更美好

正则表达式虽然自由,但自由久了容易乱套。所以,分组 () 机制就诞生了:把一段东西抱在一起,作为一个整体来处理。

就像开会拉个小组讨论,大家更有组织感!


():普通分组

普通的圆括号 () 有两个作用:

  1. 把内容打包成一个整体。比如你想重复一整块内容,就可以打包起来再用 *+ 等量词。
  2. 捕获匹配的内容。也就是可以把匹配到的小块内容拿出来以后用。

比如:

  • 正则 (ab)+ 可以匹配:
    • ab
    • abab
    • ababab

每次匹配的是整个 ab,而不是单独的 ab

捕获的内容也可以在替换文本时用,比如 \1 代表第一个括号里的内容,超实用!(后面举例)


(?:):非捕获分组

有时候你只是想分个组,方便管理,但你压根不想“捕获”它。
这时就用 (?:...) —— 非捕获分组!

比如:

  • 正则 (?:cat|dog)+ 会匹配:
    • cat
    • dog
    • catdogcat

但不会单独记录是哪个子串,纯粹为了逻辑清晰,不留证据,不搞记录

总结:

  • ():分组 + 捕获
  • (?:):分组,不捕获,低调做人。

其他好用的家伙们:|、转义符、^、$

到了这里,正则的武器库里还剩下一些必学技能,咱们逐个拿出来摆一摆!


|:二选一?多选一!

竖线 | 表示或者(OR)。

比如:

  • 正则 cat|dog 可以匹配 catdog,有点像语文里的“选择题”。

配合括号使用效果更佳,比如:

  • (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 可以匹配 HelloHELLOhElLo……

简单理解:我不在乎你是大写小写,心灵契合才是王道。


m:多行模式(multiline)

默认情况下,^$ 只匹配整串文本的开头和结尾。

加上 m 标志,^ 匹配每一行的开头,$ 匹配每一行的结尾

比如:

  • 文本是:helloworld

  • 正则 /^world/m 可以匹配到 world,因为现在每一行都有自己的“起点”了。

简单理解:每一行,都值得被认真对待!


🎯 小结:

标志 作用
g 全局匹配,找到所有匹配
i 忽略大小写匹配
m 多行模式,行行高亮

小提醒:
有些语言(比如 Python 的 re 模块)用 re.IGNORECASEre.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 非贪婪 简明对比:

匹配方式 表达式 行为
贪婪匹配 <.*> 尽可能多,吃到最后一个 >
非贪婪匹配 <.*?> 尽可能少,遇到第一个 > 就收手

小总结一句话:

贪婪匹配:全世界我都要!
非贪婪匹配:遇到就好,不强求~


写在最后:正则之路,刚刚开始!

恭喜你一路披荆斩棘,走到这里!
如果你认真看完了这篇正则表达式指南,那么恭喜你,已经比绝大多数程序员更懂正则了!(没开玩笑)

当然,正则表达式就像一门武功,只看不练,是学不会六脉神剑的。
真正的掌握,需要多敲、多练、多踩坑、多吐槽。

为了让你快速升级打怪,我特别推荐一个超棒的网站:

👉 RegexLearn - 正则表达式在线学习

这个网站简直就是正则界的小甜品店!

  • 每一课短小精悍,轻松易懂
  • 支持中文界面,不用翻词典
  • 在线就能练习,做题刷经验
  • 还能直接看到匹配效果,快乐加倍!

最后,送你一句话作为结尾:

正则表达式不是魔法,它只是复杂得像魔法而已。
慢慢来,哪怕今天只学会了一个.,也已经比昨天更强啦!


(完)