正则训练营 | 2.正则中的物料:特殊单字符 + 字符组 + 量词 + 量词模式


theme: cyanosis
highlight: atom-one-dark

本文正在参加「金石计划 . 瓜分6万现金大奖」

王志远,微医前端技术部

学习脉络

​正则无论是用来获取某段内容,还是看是否匹配某个规则,说到底其实就是对文本信息的获取;对一个庞大东西的处理思路就像做递归

  1. 找到最小单元
  2. 设定重复

​这样就能避免被大数据量搞到晕头转向了,文本也一样,本质是字符,对字符处理的最小单元自然是单个字符,而重复规模则用量词进行控制,这就迎来了本文的主角团:特殊单字符/字符组和量词。

​字符组决定选择单个字符的规则;量词则决定这个选中规则要处理多少个字符。

​让我们详细展开,就会发现很多像\d\w之类的老朋友。

特殊单字符(字符组)

基础使用

​对于单字符选择而言,在正则中的术语被称为字符组,接下来我们都会用这个术语,但不要被迷惑,它并不是匹配一组数据,而只是匹配一组数据中的一个字符,这点很关键。

语法: [xxx]

匹配规则: 目标文本需包含【任意一个包含在括号中的元素】

**获取信息规则:**将获取第一个【任意一个包含在括号中的元素】

举例: /[abc]/这个正则将匹配 a、b、c 中的任何一位且只有一位,默认匹配第一位,使用测试平台会得到如下结果

取反逻辑

​正常使用是范围内选取,不过也会出现【除了这些之外】的范围选取,这时就需要用到取反了

语法: [^xxx]

匹配规则: 目标文本需包含【任意一个不包含在括号中的元素】

**获取信息规则:**将获取第一个【任意一个不包含在括号中的元素】

举例: /[^abc]/这个正则将匹配非 a、b、c 中的任何一位且只有一位,默认匹配第一位,使用测试平台会得到如下结果

常用字符组

​八二原则同样适用在字符组中,有很多常见的匹配规则,没必要重复写,于是正则就把这些特殊字符组分别取了个别名

主要分为如下【3+1】概念,三对+一个

使用符号 规则 对应字符组
\d 单个数字 [0-9] (digit 数字)
\D 单个任意非数字 [^0-9]
\w 单个任意数字字母下划线 [0-9a-zA-Z_] 数字字符下划线
\W 单个任意非数字字母下划线 [^0-9a-zA-Z_] 非单词字符
\s 单个任意空白符 [ \t\v\n\r\f] 表示空白符 (空格、水平制表符、垂直制表符、换行符、回车符、换页符)
\S 单个任意非空白符 [^ \t\v\n\r\f]
. 单个除换行符外任意字符 [^\n\r\u2020\u2029] 通配符 除换行符、回车符、行分隔符、段分隔符外任意字符

其他常用字符组

对应字符组 规则
[\d\D] | [\s\S] | [\w\W] | [^] 任意字符
空白字符

​这类字符比较特殊,单独拎出来,方便后期直接使用

​到此,我们也就知道了如何去匹配单个字符,这就是我们的“最小规模”,但一个个匹配肯定是不行,那正则将冗长到难以忍受,所以我们还需要重复,这就是量词的作用。

量词

​这个没有很复杂的东西,唯一要注意的是,正则默认是只匹配一次的,即一次匹配完后就算后文还有符合的内容也不再获取,这涉及到修饰符g,后面再补充;

​看案例就行,我们还是以[abc]作为字符组最小单元来演示。

*号:0-n 次

正则: /[abc]*/

匹配规则: 目标文本无需包含【任意一个包含在括号中的元素】,此匹配规则一定成立

**获取信息规则:**将获取第一段【包含在括号中且连续的元素组】

+号:1+n 次

正则: /[abc]+/

**获取信息规则:**将获取第一段【包含在括号中且连续的元素组】

匹配规则: 目标文本需包含【至少一个包含在括号中的元素】

?号:0 次或 1 次

正则: /[abc]?/

**匹配规则:**此匹配规则一定成立

**获取信息规则:**将获取第一个【任意一个包含在括号中的元素】

{}符号:精确控制次数

​上面其实都属于特殊案例,我们可以通过{}精确控制匹配次数,主要有三个用法

  • {m}:必须出现 m 次
  • {m, n}:可以出现 m-n 次
  • {m,}:至少出现 m 次

我们对这三种情况进行演示,动图如下

至此,我们就完成了对量词规则的学习

量词模式

​对于量词而言,既然存在一个范围,那就存在是取最大值还是最小值的分歧,计算机是死板的,一定要明确的规则才能正确运行,这就涉及到正则模式的设定,在正则中对于这个问题存在三种模式

  • 贪婪模式:默认,会尽可能匹配多的内容
  • 懒惰模式:量词后面加个?,会尽可能少匹配内容
  • 独占模式:量词后面加个+,不触发回溯动作

举例见模式区别

测试用例: aaabb

测试正则:

  • 贪婪模式:/a*/
  • 懒惰模式: /a*?/
贪婪模式:/a*/

匹配过程:

匹配结果:

对应输出结果: ['aaa','','','']

懒惰模式: /a*?/

匹配过程:

匹配结果:

对应输出结果: ['','a','','a','','a','','','']

补充案例

特殊的模式:独占模式

​贪婪和懒惰很好理解,面向的是量词的范围不确定;量词如果是属于一个不定范围的话(如 1-3),贪婪模式会默认多的去匹配;而懒惰模式则会尽可能少的去匹配。

​而独占模式很特殊,它面向的是回溯问题;在量词的范围不确定情况下,无论是贪婪模式还是懒惰模式,都很有可能发生回溯现象使得匹配成功,而独占模式下则不会发生回溯,会直接返回匹配失败。

**注意:**很多地方是不支持此模式的,需要安装regex模块

下面是python中的处理方式

// 先安装 regex 模块 pip install regex

import regex

regex.findall(r'xy{1,3}z', 'xyyz') # 贪婪模式
regex.findall(r'xy{1,3}?z', 'xyyz') # 懒惰模式
regex.findall(r'xy{1,2}+yz', 'xyyz') # 独占模式

回溯现象

​回溯是指量词的范围不确定情况下,正则引擎会

  • 在贪婪模式下,尽可能多的匹配,一旦发生不匹配,就回退跳出此字符组的处理,改为处理下一个字符组,尝试是否匹配
  • 在懒惰模式下,尽可能少的匹配,改为处理下一个字符组,一旦发生不匹配,就回退跳出此字符组的处理,尝试是否匹配

回溯案例分析

测试用例: xyyz

测试正则:

  • 贪婪模式:/xy{1,3}z/
  • 懒惰模式: /xy{1,3}?z/
  • 独占模式: /xy{1,3}+z/
贪婪模式下回溯现象

​在匹配时,y{1,3}会尽可能长的匹配

  1. 在完成匹配xyy时,会继续尝试匹配第三个y,但匹配到了z
  2. 失败了,于是正则就会向前回溯吐出z
  3. 然后用正则中的z去匹配,匹配成功
  4. 处理结束

懒惰模式下回溯现象

​在匹配时,y{1,3}?会尽可能短的匹配

  1. 在完成匹配xy时,就会结束这个字符组的匹配
  2. 然后用z去匹配,匹配到了y
  3. 失败了,于是正则就会向前回溯吐出y
  4. 然后用正则中的y{1,3}?去匹配,匹配成功,会再一次结束这个字符组的匹配;
  5. 然后用z去匹配,匹配成功
  6. 处理结束。

独占模式下回溯逻辑的处理

​在匹配时,y{1,3}会尽可能长的匹配,但一失败,不会回溯,而是会返回失败

  1. 在完成匹配xyy时,会继续尝试匹配第三个y,但匹配到了z
  2. 失败了,直接返回失败

正则回溯引发的血案

​这里我们挑选一个比较出名的,是阿里技术微信公众号上的发文。Lazada 卖家中心店铺名 检验规则比较复杂,名称中可以出现下面这些组合:

  1. 英文字母大小写;
  2. 数字;
  3. 越南文;
  4. 一些特殊字符,如“&”,“-”,“_”等。

阿里负责的开发在开发过程中使用了正则来实现店铺名称校验,如下所示:

^([A-Za-z0-9._()&'\- ]|[aAàÀảẢãÃáÁạẠăĂằẰẳẲẵẴắẮặẶâÂầẦẩẨẫẪấẤậẬbBcCdDđĐeEèÈẻẺẽẼéÉẹẸêÊềỀểỂễỄếẾệỆfFgGhHiIìÌỉỈĩĨíÍịỊjJkKlLmMnNoOòÒỏỎõÕóÓọỌôÔồỒổỔỗỖốỐộỘơƠờỜởỞỡỠớỚợỢpPqQrRsStTuUùÙủỦũŨúÚụỤưƯừỪửỬữỮứỨựỰvVwWxXyYỳỲỷỶỹỸýÝỵỴzZ])+$

可以简化为

 ^([符合要求的组成 1]|[符合要求的组成 2])+$

​需要留意的是,正则中有个加号(+),表示前面的内容出现一到多次,进行贪婪匹配, 这样会导致大量回溯,占用大量 CPU 资源,引发线上问题,我们只需要将贪婪模式改成独 占模式就可以解决这个问题。

总结

​在正则的元字符中,根据【最小规模+重复】思路

  • 通过字符组我们得知了如何设定选择单个字符的规则
  • 通过量词我们将单个字符的处理范围扩大化
  • 通过量词模式我们解决了范围的歧义问题

让我们用一个题目来强化学习吧!

文末思考

手机号的组成规则
  1. 第 1 位固定为数字 1;
  2. 第 2 位可能是 3,4,5,6,7,8,9;
  3. 第 3 位到第 11 位我们认为可能是 0-9 任意数字。

请写出匹配正则

参考答案:/^1[3-9]\d{9}&/

© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容