Published on

正则表达式速记口诀

Authors

这是一个正则的助记口诀/顺口溜,让我们用30分钟时间来轻松背下难记的正则

欢迎通过issue提交更好的口诀或者补充未提到的正则表达式

首先先奉上口诀:

从前有个傻大三,

哇塞,日子过得特别难 \w\s\r\z\d\t\b\n

人傻人穷只有捡破烂

做梦都想变成大富豪,就不用再出来捡破烂 \W\S\D\B

每天哼着歌儿出去捡破烂

两手叉着腰,左手捡了右手又捡 a|b

你问他捡到钱没 ?

左手回答得还行,右手被问得头冒金星 + *

只有装进袋子把数字标 {n}

关上门来把金银财宝往屋里搬 [abc]

脑袋儿被门夹了不知道啥东西要选 [^abc]

钢镚儿一碗,铁镚儿一碗 (abc)

没问题的打个标记,好知道有好多钱 (?<name>)

有问题的放一边,挤破脑袋也还要向前再看一眼 (?=exp) (?!exp) (?<=) (?<!)

剩下的铜钱儿用线串 [a-zA-Z0-9]

一不小心遇到地头蛇,一顿拷问被吓得赶紧溜回家 +? *?

释义

嘴巴儿尖尖,句句都是钱

尖尖代表^符号,钱代表$符号

傻大三这个人一张嘴说话,句句都是以钱收尾

所以我们get到了

正则表达式描述示例
^匹配字符串的开始^acb 匹配以abc开头的字符串
$匹配字符串结尾abc$匹配以abc结尾的字符串

哇塞,日子过得特别难.

在正则里面有许多的元字符,这里收集了比较常用的一些,取元字符的来当声母,用来助记这些元字符,当然,我们更明白元字符所对应的英文单词,就无须死背元字符所对应的的内容了.

所以我们get到了一大串的元字符

代码描述对应文字对应单词
\w匹配一个单词的组成部分,字符,数字,下划线.(是否匹配中文视操作系统和应用环境而定)Word
\s匹配空白符Space
\r回车Enter
\z字符串结尾(类似$,但不受处理多行选项的影响)?
\d数字Digital
\ttab制表符Tabulator key
\b边界,单词分界位置Boundary
\n换行符Line feed
.点号(相当于句号),在一个段落中,以它结尾,它包括了前的各种符号,但不匹配换行符.因为换行就是新段落了.

做梦都想变成大富豪,就不用再出来捡破烂

变成大富豪,表示元字符变成了大写字母

就不用再出来破烂,表示不再匹配,相当于对以上小写的元字符取反

所以我们get到了

代码描述
\W匹配一个非单词的组成部分,字符,数字,下划线.(是否匹配中文视操作系统和应用环境而定)
\S匹配非空白符
\D非数字
\B非边界,单词分界位置

两手叉着腰,左手捡了右手又捡

这一表示正则里面的分枝匹配,形如a|b表示两边或的匹配关系,即可以匹配a也可以匹配b

傻大三捡垃圾,左边有就左手捡到,右边有,就右手捡起来.

你问他捡到钱没?左手回答得还行,右手被问得头冒金星,只有装进袋子把数字标

问到捡到钱没,这是一个问句,回答应该是是与否,代表0或者1,所以有正则?匹配0次或者1次

因为他左手刚刚捡到了一块垃圾,所以他傻傻地也知道左手捡到有东西,在数学里面就是用正号+表示,至少有一个.

被问得头冒金星,他也不知道右手到底有没有捡到有东西了.金星用*表示,可能没有捡到有,也可能捡到很多

只好数一下装进袋子里面,用数字标上有多少.袋子用{}表示,数字用n表示有多少个.得到完整的正则表达式{n}即表示前面的匹配n

于是我们get到的正则的匹配模式

代码描述
+匹配至少一次
*匹配0次或者多次
?匹配零次或者一次
{n}匹配n次
{n,m}匹配n到m次
{n,}匹配n次或者更多次,没有m代表无穷尽

关上门来把金银财宝往屋里搬,脑袋儿被门夹了不知道啥东西要选

关上门表示[]符号,关在里面的金银财宝,都可以往屋里搬,所以表达式[abc]表示匹配abc任意一个.

结果傻大三的头^不小心被门给夹住了,不知道要这些金银财宝了.所以表达式[^abc]表示匹配不是abc这些字符的.

钢镚儿一碗,铁镚儿一碗

碗用()表示,傻大三还是知道对这些垃圾进行分类,也就是正则里面的分组,分组之后,不仅可以用于前面的匹配模式,也可以用于后面的反向引用和零宽匹配及更多的功能.

比如(abc){2}表示匹配连续的两个abcabcabc

没问题的打个标记,好知道有好多钱

傻大三对碗打个标记,以免有疑问?时才好知道有好多钱,所以有正则表达式形式(?<name>exp),打好标记之后,在后续就可以通过这个标记来识别引用了.

比如\b(?<Word>\w+)\b\s+\k<Word>\b表示匹配连续重复的单词,比如go go.如果不打标记,默认使用数字来识别,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。所以这个表达式不打标记的写法就是\b(\w+)\b\s+\1\b

有问题的放一边,挤破脑袋也还要向前再看一眼

有问题?的,有可能是真钢镚,即等号=表示,有可能不是真钢镚,即非!表示

放一边,表示只匹配了,但不参与捕获.和前面的^,$,\b一样,是属于零宽断言,它们 本身不匹配任何字符,只是对 "字符串的两头" 或者 "字符之间的缝隙" 附加了一个条件.

于是有正预测先行断言(?=exp),表示断言自身出现的位置后面能匹配表达式,比如\b\w+(?=ed\b)表示匹配以ed结尾的单词,但不包含ed;

同理有负预测先行断言(?!exp),和上面的意思相反,即不能满足表达式.比如\b\w*e(?!d)\w*\b表示匹配包含e的单词,但同时e后面不能跟着字符d.它和\b\w*e[^d]\w*\b的区别是,后者的[^d]会参与一次匹配,比如它会匹配上e,abc,因为里面的,参与了[^d]的匹配.

挤破脑袋向前<,前面说的是先行断言,即先匹配上前面的表达式.相反的,挤破脑袋往前面去就变成了后发断言.即有

正预测后发断言(?<=exp)表示断言自身出现的位置前端能匹配表达式exp

负预测后发断言(?<!exp)表示断言自身出现的位置前面不匹配表达式exp

零宽断言是先满足匹配其自身表达式后,再获取断言里面的表达式是否满足. 比如(?<=\d{4})\d+(?=\d{4})去匹配1234567890,先匹配上\d+,再判断(?<=\d{4}(?=d{4}),于是最终的匹配结果是56.

综合以上三条,我们对于正则分组,可以再总结一下

代码描述
(exp)分组匹配,后续可以通过\n进行反向引用
(?<word>exp)分组匹配命名,后续可以通过\k<word>进行反向引用
(?:exp)只分组,不生成分组编号,也不捕获.相当于只看一眼:而已
(?=exp)匹配exp前面的位置
(?!exp)匹配后面不是跟的exp的位置
(?<=exp)匹配exp后面的后面
(?<!exp)匹配前面不是跟的exp的位置

剩下的铜钱儿用线串

用线-串,表示把铜钱儿都串起来了,就是正则里面的[a-zA-Z0-9]这种表示方法,表示az的全部大小写字母和09的全部数字.

一顿拷问被吓得赶紧溜回家

遇到地头蛇拷问?,溜回家不再捡垃圾,就是正则里面的遇到?由默认的贪婪匹配变成了非贪婪(惰性)匹配.

比如对于字符串aabaab用正则a.*b会匹配上整个字符串,因为它是贪婪匹配的,里面的.*会尽可能长的进行匹配.

a.*?b则只会匹配aab.因为对于.*遇到了?号,就被变成了非贪婪匹配了.

于是结合前面的匹配模式,我们可以get到

代码描述
+?匹配至少一次,但尽可能少地匹配
*?匹配0次或者多次,但尽可能少地匹配
??匹配零次或者一次,但尽可能少地匹配
{n,m}?匹配n到m次,但尽可能少地匹配
{n,}?匹配n次或者更多次,没有m代表无穷尽,但尽可能少地匹配

参考

《正则表达式必知必会》

正则表达式30分钟入门教程

正则表达式MDN