写正则表达式的文章很容易写成两副面孔:要么是一张列满元字符的“语法大全”,看完啥也记不住;要么是一堆看起来很高端的实战代码,但完全没讲清楚“为什么这么写”。这篇我想换个路子,从正则引擎实际执行过程讲起,把语法规范、IP地址校验、grep日志过滤、字母数字组合校验这类真实需求串起来,顺便把新手最容易踩的坑也一并交代清楚。不管你是第一次接触正则,还是已经断断续续写过一些,这篇文章都值得花十几分钟读完。
1. 先搞清楚正则到底在“匹配”什么:引擎的基本工作方式
很多人学正则卡住的第一个原因,是压根没搞明白“匹配”这两个字的含义。正则表达式不是“判断一个字符串是否等于某个样子”,而是“在一个字符串里从头到尾寻找满足条件的子串”。这种寻找有自己的底层规则,理解它比背下所有元字符都重要。
1.1 一段正则执行时到底发生了什么
想象一下你在一面墙上找某个图案,你手里拿着一张镂空的卡片,卡片上的镂空形状就是你的正则表达式。你会把卡片从墙的最左端开始,先对齐,看看镂空处的图案是否完全对应;对不上,就把卡片往右挪一个字符,再对齐再看。这个“从左到右、一个个位置尝试”的过程,就是正则引擎的基本工作原理。
拿最基本的匹配举例:
code复制文本:cat123dog
正则:\d+
引擎会从文本的第一个字符c开始,尝试用\d+去匹配。\d代表数字,c不是数字,失败;接着引擎把起始位置移到a,还不是数字,失败;再移到t,失败;移到1,匹配成功,+号要求继续匹配数字,于是2、3也匹配上,直到遇到d为止。最终结果匹配到123。
这里有两个容易被忽略的细节。第一,正则默认是“部分匹配”而不是“整串匹配”。\d+能在cat123dog里找出123,但如果你想让整个字符串必须是数字,就得自己加锚点(比如^\d+$),让引擎只能从字符串开头匹配到末尾。第二,引擎一旦在某一个位置匹配成功,它会尽量往“更长”的方向匹配,这是贪婪量词的本性,后面细讲。
1.2 贪婪、懒惰与回溯:大部分“奇怪问题”的源头
正则引擎里最影响结果的两个因素,一个是量词的贪婪与否,一个是回溯机制。几乎所有“为什么我写的正则会多匹配/少匹配”的困惑,都出在这里。
所谓贪婪,是指*、+、?、{m,n}这些量词默认会尽可能多地匹配字符。比如:
code复制文本:<div>内容1</div>尾巴<div>内容2</div>
正则:<div>.*</div>
.*会一路吞掉尽量多的字符。第一次匹配时,<div>之后,.*会一直吃到字符串最后,然后再回退,寻找最后一个</div>。所以整个正则最终匹配的是从第一个<div>到最后一个</div>的整段内容,而不是你直觉里希望的两段分开的内容。
这段寻找的过程就是回溯:引擎先按最大长度尝试,发现后续匹配不上,就一点点往回退,再尝试其他路径。量词越模糊、嵌套越多,回溯的路径就越复杂,这也是某些正则表达式在长文本上跑得极慢甚至卡死的根本原因。
如果想改变这种行为,有两个办法。一是用懒惰匹配,在量词后面加一个?,变成<div>.*?</div>,这样.*?会尽可能少地匹配,遇到第一个</div>就停下来,于是两个div内容会分别匹配;二是使用更严谨的字符类,比如<div>[^<]*</div>,直接排除掉尖括号,从源头上减少回溯。
提示:写正则时多想一想“这个位置到底允许出现哪些字符”,比用一个万能的
.加懒惰匹配要可靠得多。字符写得越精确,回溯越少,性能越稳。
明白了引擎的工作方式,后面那些语法、实战、坑点就都好理解了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 语法规范速通:字符类、量词、分组与锚点
这一节不是把语法大全抄一遍,而是把日常写正则最常用的几块语法,逐个讲清楚“是什么、怎么用、常见坑”。你接下来读实战案例时,所有代码都会反复用到这里的知识。
2.1 字符类:一对方括号里的各种门道
方括号[],专业术语叫字符类,它代表“匹配方括号内列出的任意一个字符”。这是正则里最基础也最好用的构造。
[abc]:匹配a、b、c中的任意一个。[a-z]:匹配a到z范围内的任意一个小写字母。[0-9]:等于\d,匹配任意一个数字。[a-zA-Z0-9_]:匹配字母、数字、下划线中任意一个,这在很多场景下等于\w。
字符类里有个反义操作,在左括号后加一个^,比如[^0-9]表示匹配任意一个非数字字符。这在实际开发里非常常用,比如过滤掉所有数字、校验字符串里不包含特殊字符等。
这里有一个新手经常犯的错:在字符类里写多个字符,以为匹配的是“字符串”。比如[abc]不是匹配字符串abc,而是匹配a或b或c中的任意一个。如果想让三种组合都出现,得写abc这个字符串本身,或者用分组加|。
另一个坑是转义规则。在字符类里,大部分元字符都失去了特殊含义,直接写就行。比如[.]匹配的就是字面意义上的点号,不用加反斜杠。但^(出现在非首位)、-(表示范围)、]、\这些还是需要小心处理,-放在字符类开头或结尾时表示普通横杠,放在中间则可能被当成范围连接符。
2.2 量词:控制重复次数时的细节
量词控制的是“前面的表达式出现多少次”,常见的有:
*:0次或多次。+:1次或多次。?:0次或1次。{n}:恰好n次。{n,}:至少n次。{n,m}:n到m次之间。
你可能已经注意到了,元字符本身也可以用量词来修饰。\d+表示一个或多个数字,[a-z]*表示零个或多个小写字母。这种“字符类+量词”的组合,就足够应付一大半的格式校验场景了。
量词的一个细节是它只修饰“紧挨着它前面的那个元素”。比如ab+匹配的是a后面跟一个或更多b,也就是ab、abb、`abbb
