击破正则的痛点难点

3,574 阅读3分钟

作者:李嘉豪 lijiahao043@ke.com

摘要

正则其实并不是一个特别有技术含量的知识体系,它的作用无外乎以下 3 点,分析字符串是否满足某种模式 test,替换指定的子串 replace,以及捕获一处或多处目标子串 exec。本文需要你事先对正则有一定程度的了解,在这个基础上,帮你进一步巩固正则里面那些不太友好的使用方法。

捕获和非捕获 ()(?:)

捕获就是你用 exec 能抓到的字符,非捕获就是用 exec 抓不到的字符,我们来个例子:

2019-11-1;20190112;2017 2 8;

写一个正则,能把上述例子中,所有的年月日都捕获到。

(\d{4})[-\s]?(\d{1,2})[-\s]?(\d{1,2}))

把你想匹配的子串放到小括号里,其他的交给正则,它会把结果依次放到数组里面。它们分别会出现在 index = 1 2 3 的位置。

那么假如我不想捕获月怎么办,很简单,在中间的小括号前加上 ?=

(\d{4})[-\s]?(?:\d{1,2})[-\s]?(\d{1,2}))

还有一个问题,假如你写的正则有嵌套,那么怎样判断它在正则结果数组里面是第几个呢?有一个很简单的方法,看它对应的左侧小括号是第几次出现。我们还拿这个例子,假如,我们把 月-日 整体用小括号括起来。

(\d{4})[-\s]?((\d{1,2})[-\s]?(\d{1,2})))

请问 月-日 的 index 是多少?用本文教你的方法,数左侧小括号个数,答案是 2。

环视 (?=)(?<=)(?!)(?<!)

这玩意也被叫做正向预查,负向预查。它怎么叫不关键,重要的是,你是否理解,会用。

  • (?=) 后面必须跟着 xx 的字符串
  • (?<=) 前面必须有 xx 的字符串
  • (?!) 后面不能跟着 xx 的字符串
  • (?<!) 前面不能有 xx 的字符串

匹配 $123$; $xxxx$as; $xxx $,不匹配 $$dddd$; $$ asafa $$; sdfs

意思就是说,让你只匹配夹在两个单 $ 里面的内容

(?<!\$)\$(?!\$).+(?<!\$)\$(?!\$)

看起来有点复杂,其实前面和后面是一样的。我们只看

(?<!\$)\$(?!\$)

从中间看,它的意思是,有一个 $,前面不能是 $,后面也不能是 $。其实就想表达这么个意思。

我们再来个例子

匹配密码,至少 8 位,至少一个小写字母,一个大写字母,一个数字。

(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,}

?= 意思因为是后面必须跟着啥,那么开头啥也没有,啥也没有后面必须跟着几位数(或者没有)外加一个大写字母,啥也没有后面必须跟着几位数外加一个小写字母,啥也没有后面必须跟着几位数外加一个数字。同时,这个密码至少 8 位。这就是正则翻译成文字的意思。

a@gmail.com; abc@163.com 不匹配 xxx@ke.com

@(?!ke) 意思是 @ 后面不能跟着 ke。

回溯 \n

回溯的意思是用一个数字代表之前捕获的某个元素,并且该元素的 index = n

我们来举一个例子:

匹配 12321;899812;99099;11233219 不匹配 1234;7395;12902

可以看出,至少有两个数连续回文我们就匹配,那么我们可以这么写

(\w)(\w).*(\2)(\1)

意思是第二个匹配的字符先出现,第一个匹配的字符后出现。

总结

这三个地方应该是正则里面最难的地方了,基本上你能熟练掌握这三个技术点,正则对你来说就没有什么难度了。不过我的确还见过一个反人类的正则题,它让你匹配质数个 x。这玩意不用正则其实很简单,用了正则反而变得比较困难。有兴趣的同学可以试一试。有愿意探讨的可以找 lijiahao043,不定时回复。