资讯专栏INFORMATION COLUMN

硬刚正则表达式的心得总结

txgcwm / 990人阅读

摘要:近几日对自己一直不太擅长的正则表达式做了一次全面的扫盲。量词在正则中,通常要表示一个表达式匹配的数量,这个时候量词就登场了。而正则默认是贪婪模式的。正则会默认对捕获组分配组数。

近几日对自己一直不太擅长的正则表达式做了一次全面的扫盲。心疼自己之余还是有一些收获吧,在这里做一个比较零散的总结,整理一些对理解正则比较有利的点。
一、"?"

你没有看错,就是黑人问号中的问号,这个字符在正则里面算是一个入门中很容易被带偏的点了。首先要知道什么是正则中的量词。

1.量词

在正则中,通常要表示一个表达式匹配的数量,这个时候量词就登场了。
主要会使用以下几个量词

/(w)*/.exec(str)  // 匹配任意次
/(w)+/.exec(str)  // 匹配一次到多次
/(w)?/.exec(str)  // 匹配零到一次(记住这里问号的用法!)
/(w){2, 4}/.exec(str) // 匹配两次到四次
/(w){2, }/.exec(str)  // 匹配两次以上

我们可以发现,在这里"?"作为一个量词来使用,表示匹配零到一次

接下来要理解下一个概念:贪婪匹配

2.贪婪匹配

搜了一下wiki,貌似没有相关的词条,通俗的解释,贪婪匹配模式下,会尽可能多地匹配满足条件的字符。而正则默认是贪婪模式的。

举个例子。比如我要匹配"suuuuuuuuuuck"字符中的s和k中间的字符。并没有什么问题。

let result = "suuuuuuuuuuck".match(/s(.*)k/)[1]
// uuuuuuuuuuc

但是我现在要搞事情,要你在"suuuuuuuuuuck duck"字符串中匹配相同的字段,同样的表达式会匹配到以下的结果,因为此时的正则是贪婪的,它一定会匹配到无法匹配的时候才休止。

// uuuuuuuuuuck duc

这时候就需要手动开启非贪婪模式了

let result = "suuuuuuuuuuck duck".match(/s(.*?)k/)[1]
// uuuuuuuuuuc

区别是在量词后加了个问号,这时候该捕获组就算是开启了非贪婪模式了。

按照上面的理解,如果你是一个新手,肯定会有所疑惑,量词(*)后面跟着一个量词(?),这是什么鬼意思,这么反人类的?

其实,这里就涉及到"?"的第二个用法了,即当它跟在一个量词背后的时候,表示该表达式开启了非贪婪模式,即对表达式尽可能少地匹配结果。所以,对应的,配合量词使用,会产生以下结果。

"*?": 可以匹配任意多次,但是尽量少匹配。

"+?": 至少必须匹配一次,但是尽量少匹配。

"{m, n}?": 至少必须匹配m次,最多只能匹配n次,但是尽量少匹配。

"{m, }?": 至少必须匹配m次,但是尽量少匹配。

思考题:所以,"??" 应该如何匹配呢?
二、捕获组

正则匹配除了验证一个字符串是否符合条件外,还可以从中提取我们所需要的信息。比如,我们得到了一个"新中国成立于1949-10-01"的字符串,作为一个爱国人士,我们要把这个年月日提取出来谨记于心。所以我写了一个正则,获得的结果如下

这里提取的操作就需要通过小括号进行捕获。正则会默认对捕获组分配组数。

"新中国成立于1949-10-01".match(/(d{4})-(d{2})-(d{2})/)
// ["1949-10-01", "1949", "10", "01", index: 6, input: "新中国成立于1949-10-01", groups: undefined]

我们也可以忽略某些分组"(?:exp)",这样正则就不会为为其分配组数。

"新中国成立于1949-10-01".match(/(d{4})-(d{2})-(?:d{2})/)
// ["1949-10-01", "1949", "10", index: 6, input: "新中国成立于1949-10-01", groups: undefined]

假如我们有一个叠词判断的需求,验证一个词语是不是"ABA"格式的,我们可以这么做

// 首先汉字的unicode范围是u4e00-u9fa5
// 这里我们首先对第一个字符进行了捕获,组数为1
// 然后我们后面通过"1"的方式去复用捕获组,这样就意味着匹配到了相同的字符,也就达到了限制的目的。

/([u4e00-u9fa5])[u4e00-u9fa5]1/.test("是不是")
// 当然是true

要记住下标对人类来说还是挺麻烦的,可以说完全没啥可读性,当然正则也提供了为分组命名的方式

"新中国成立于1949-10-01".match(/(?d{4})-(?d{2})-(?d{2})/)
// 我们可以发现,这时候捕获组不仅拥有组数,同时groups属性不为空了。
// ["1949-10-01", "1949", "10", "01", index: 6, input: "新中国成立于1949-10-01", groups: {…}]
// 展开groups 是这样的
// {year: "1949", month: "10", date: "01"}

/** 当然命名捕获组也是可以使用的 */
// (?exp) 命名捕获组
// k 引用

// 还是叠词的那个例子
/(?[u4e00-u9fa5])[u4e00-u9fa5]k/.test("是不是")
现在有一个需求,匹配出英文语句"I"m singing while you"re dancing"中所有带有ing后缀的单词(不包含ing)。要想拿到danc 和 sing,我们需要用到零宽断言。
三、零宽断言

零宽断言用于查找某些内容之前或之后的东西,只指定一个位置,本身并不占据字符,这也是为什么我们称之为零宽度

对于表达式表示肯定,我们称之为正向,反之称之为负向,(注意,这里的正负指的是对条件的肯定和否定,而不是匹配的方向。)

而对于匹配的方向而言,我们有另外一种称呼,对向后匹配的称之为预测先行,向前匹配的称之为回顾后发

所以,对应的四种组合分别是

(?=exp) 零宽度正预测先行断言(断言自身出现的位置后面能匹配exp)

(?!exp) 零宽度负预测先行断言(断言自身出现的位置后面不能匹配exp)

(?<=exp) 零宽度正回顾后发断言(断言自身出现的位置前面能匹配exp)

(?

目前的js引擎对回顾后发断言的实现还不完全,就我所知在chrome能成功使用,但是在nodejs环境下是不识别的。

现在我们从引言中的例子来实践一下

"I am singing while you"re dancing".match(/([a-zA-Z]+)(?=ing)/g)
// 我们忽略前面不满足的匹配,直到index = 4时,s为单词边界,满足条件
// 而第一个捕获组是贪婪的,他会首先匹配到整个singing,然后将掌控权交给(?=ing),singing不满足匹配 "singinging"
// 于是开始回溯到单词 singin,继续断言, 匹配到的下一个字符为"g", 不满足"singining", 又开始回溯到"singi"...
// 直到回溯到"sing"时,断言后面有一个ing,并且是一个单词边界,于是"singing"满足条件,这时候我们的正则匹配到了第一个结果。
// 由于零宽断言是不消费字符的,所以我们得到整个表达式匹配的第一个结果是"sing"
// 于是引擎以同样的方式向后面的位置查找,得到了danc
// ["sing", "danc"]

我们现在看一下怎么使用负向断言,假如我们有一个系统,3月25号要进行维护,不能使用了,这时候有用户要办理业务,选择日期的时候我们要过滤3月25日这一天,所以产品经理要你临时加上一条规则限定。

选择后日期输出的格式是"yyyy-mm-dd",这时候我们可以这么写正则

/(?!2018-03-25)(d{4})-(d{2})-(d{2})/.test("2018-03-11")
// true 通过验证
/(?!2018-03-25)(d{4})-(d{2})-(d{2})/.test("2018-03-25")
// false

用(?<=exp) 找出 "beep name=wanglihong abcdefg"

"beep name=wanglihong abcdefg".match(/(?<=name=)(w+)/)
// ["wanglihong", "wanglihong", index: 10, input: "beep name=wanglihong abcdefg", groups: undefined]

提取a标签的属性的同时,通过(?

var template = "点击跳转"
template.match(/(w+)=(?

摸透了零宽断言,正则的能力也就算上了一个台阶了,当然还有平衡组这种操作,因为在js不支持,所以就暂时不讨论了。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93514.html

相关文章

  • 正则系列——JavaScript正则达式入门心得

    摘要:对前端来说,使用的场景不多,但是像微信端的对话系统的表情包,就使用到了一个特定的规则。我是一个前端,工作年了,现在失业,想进入腾讯工作,这是我的联系方式这个正则虽 我发现有个别字符被这个编辑器给刷掉了,但是灰色区域显示正常,以灰色区域代码为准 什么玩意? 在我刚开始学习编程的时候,就听过正则了,也听说正则很牛逼,懂正则的更牛逼。但是苦于没有人指点,也没有使用正则的场景,自己看教程又懵逼...

    DevWiki 评论0 收藏0
  • 从简历被拒到收割今日头条 offer,我用一年时间破茧成蝶!

    摘要:正如我标题所说,简历被拒。看了我简历之后说头条竞争激烈,我背景不够,点到为止。。三准备面试其实从三月份投递简历开始准备面试到四月份收,也不过个月的时间,但这都是建立在我过去一年的积累啊。 本文是 无精疯 同学投稿的面试经历 关注微信公众号:进击的java程序员K,即可获取最新BAT面试资料一份 在此感谢 无精疯 同学的分享 目录: 印象中的头条 面试背景 准备面试 ...

    tracymac7 评论0 收藏0
  • 从简历被拒到收割今日头条 offer,我用一年时间破茧成蝶!

    摘要:正如我标题所说,简历被拒。看了我简历之后说头条竞争激烈,我背景不够,点到为止。。三准备面试其实从三月份投递简历开始准备面试到四月份收,也不过个月的时间,但这都是建立在我过去一年的积累啊。 本文是 无精疯 同学投稿的面试经历 关注微信公众号:进击的java程序员K,即可获取最新BAT面试资料一份 在此感谢 无精疯 同学的分享目录:印象中的头条面试背景准备面试头条一面(Java+项目)头条...

    wdzgege 评论0 收藏0
  • vue项目部署在IIS上面心得

    摘要:一般在做前后端分离的时候,前端服务器用的都是,可是公司项目是需要运行在上面的,所以综合考虑之下用比较好一些,然而这方面的资料不如那么多,所以就想记录一下这段时间遇到的坑,以防自己以后再遇到的时候忘记了,这样我可以翻出来看看。 一般在做前后端分离的时候, 前端服务器用的都是nginx,可是公司项目是需要运行在windows server上面的,所以综合考虑之下用IIS比较好一些,然而这方...

    Y3G 评论0 收藏0

发表评论

0条评论

txgcwm

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<