正则表达式里问号的作用

By
写代码

正则表达式一直是我的弱项,一来因为用的比较少,所以每次碰到的时候,脑子里的两个小人儿就开始吵架:“又碰到了,又不会了,赶紧学习一下吧。”“不会也没什么,不经常用,再说split/indexOf也一样能达到目的,打游戏吧”,然后我全境封锁2就满级了。

正则里问号主要用在三个地方:量词、分组、断言

量词

问号把默认的贪婪量词(greedy quantifier)变为懒惰量词(lazy quantifier)

量词(quantifier)指*/+/{n}/{n,m}这些,默认地,他们是贪婪量词,即,往多了匹配,加问号变懒惰,即,往最少了匹配,举个例子

用法是在一个量词后紧跟一个问号,形如

断言

问号用于正向/反向肯定预查,属于断言(assert)

我疑惑的点在于:肯定预查和非捕获性分组,这两个概念有什么区别?

回想一下,产生这个疑问的主要原因是,很多资料上把非捕获性分组和肯定预查放到了一起,比如mdn(中文,英文已经区分了断言assertions和分组groups)

reg-exp-in-mdn
MDN中的分组和预查的相关描述

其实这是两个概念,非捕获性分组属于“分组(group)”,而预查属于“断言(assert)”。而预查属于非捕获性分组。

混淆导致的问题是,我无法区分:在非捕获性分组这个大概念下,预查到底匹配了什么?

答案是:只是向前或向后看一下,并不匹配内容。或者说,匹配了一个“位置”(零宽匹配)

look-and-behind
正向预查和反向预查

可以看出:

正向预查向前(右)匹配,即先取得表达式右边的字符,然后站在所有空字符的地方,往右侧看,找出所有右侧是a的,并返回空字符的位置(下标0-0和6-6);

反向预查向前(左)匹配,即先取得表达式左边的字符,即站在所有空字符的地方,往左侧看,找出所有左侧是a的,并返回空字符的位置(下标1-1和7-7)。

所以,如果在正向预查左侧,反向预查右侧,带上一个字符,那么就是平常使用的结构:

这里是反向预查,先找到表达式右边的字符,即所有的s,然后向左(多加的<指向左,可以理解为向左)看,如果发现左边是字符a,那么就返回这个s。

看起来,他匹配了一个位置,为什么属于断言呢?

不同于开头(^)和结尾($)的匹配,预查实际上是匹配了字符,但是不返回字符,而是返回了查询结果:匹配或者不匹配。由于这个结果是断言,所以把它归类到了“断言”里。

预查有什么用呢?

从它的定义可以看出来,这个语句里的字符“非出现不可”,所以可以用于判断字符串里是不是包含了约定内容。比如,密码强度验证:

检查过程:
首先,这里使用的是正向预查,要取表达式左侧的内容,这里是空。
然后,检查所有空位置右侧,是否满足3个条件,.*\d匹配到以数字结尾的整个字符串,其他同理。
最后,[A-Za-z\d]{8,} 检查字符串长度是否大于8,且仅是3种字符的组合。

如何理解捕获组和非捕获组?这属于“分组”的范畴。

分组

问号主要用于命名分组(Named capturing group)和非捕获性分组(Non-capturing group)

通常,我们会用小括号来分组,然后分组内容会放入匹配组返回,默认的,小括号是捕获组。

有捕获组,就有非捕获组。

非捕获组,就是小括号内容,不会放入返回的匹配组。

为了区分不同的分组,加入了命名分组,结果放入groups里

看了一眼书架,发现我竟然有一本吃灰的《正则指引》……

0

Comments: 6

  1. 打游戏的话也可以去玩 Regex Golf 啊 https://alf.nu/RegexGolf
    虽然我自己才到匹配素数那里就被打爆脑袋了……

    07月11日
  2. 专,太细节的东西我就会头大,加油。

    07月15日
  3. 到现在正则都不太会,搞下各语言不尽相同,如 php 和 js 的

    08月30日
  4. 这个,记住几个固定用法就好了吧。我也是学 Python 才重新把正则弄明白的。

    09月23日

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

:razz: