在一些场景下,我们不需要回溯,匹配不上返回失败就好了,因此正则中还有另外一种模式,独占模
式,它类似贪婪匹配会尽可能多地去匹配,但匹配失败就结束,不会进行回溯,因此在一些场合下性能
会更好,具体的方法就是在数量词后面加上加号 + 。
独占模式下:
regex = "xy{1,3}+yz"
text = "xyyz"
y{1,2}+尽可能多的匹配前两个y,不回溯导致正则z前面的y匹配不上:
python的标准库re并不支持独占模式,会报错:
>>>importre
>>>re.findall('xy{1,3}+z','xyyz')
Traceback(mostrecentcalllast):
File"<stdin>",line1,in<module>
File"D:\Anaconda3\lib\re.py",line223,infindall
return_compile(pattern,flags).findall(string)
File"D:\Anaconda3\lib\re.py",line286,in_compile
p=sre_compile.compile(pattern,flags)
File"D:\Anaconda3\lib\sre_compile.py",line764,incompile
p=sre_parse.parse(p,flags)
File"D:\Anaconda3\lib\sre_parse.py",line930,inparse
p=_parse_sub(source,pattern,flags&SRE_FLAG_VERBOSE,0) File"D:\Anaconda3\lib\sre_parse.py",line426,in_parse_sub notnestedandnotitems))
File"D:\Anaconda3\lib\sre_parse.py",line654,in_parse
source.tell()-here+len(this))
re.error:multiplerepeatatposition7
python上要使用独占模式需要安装regex 模块:
pip install regex
再次测试:
>>>importregex
>>>regex.findall(r'xy{1,3}z','xyyz') #贪婪模式
['xyyz']
>>>regex.findall(r'xy{1,3}+z','xyyz')#独占模式
['xyyz']
>>>regex.findall(r'xy{1,2}+yz','xyyz')#独占模式[]
独占模式的典型应用:
独占模式性能比较好,可以节约匹配的时间和 CPU 资源,但并不是所有的场景都适用,否则也不会出
现python标准库不支持的场景。下面我们看一个适合适用独占模式的场景。
阿里技术微信公众号上的发文。Lazada 卖家中心店铺名检验规则比较复杂,名称中可以出现下面这些
组合:
- 英文字母大小写;
- 数字;
- 越南文;
- 一些特殊字符,如“&”,“-”,“_”等。
负责开发的小伙伴在开发过程中使用了正则来实现店铺名称校验:
^([A-Za-z0-9._()&'\-]|
[aAàÀảẢãÃáÁạẠăĂằẰẳẲẵẴắẮặẶâÂầẦẩẨẫẪấẤậẬbBcCdDđĐeEèÈẻẺẽẼéÉẹẸêÊềỀểỂễỄếẾệỆfFgGhHiIìÌỉ ỈĩĨíÍịỊjJkKlLmMnNoOòÒỏỎõÕóÓọỌôÔồỒổỔỗỖốỐộỘơƠờỜởỞỡỠớỚợỢpPqQrRsStTuUùÙủỦũŨúÚụỤưƯừỪử ỬữỮứỨựỰvVwWxXyYỳỲỷỶỹỸýÝỵỴzZ])+$
这个正则比较长但很好理解,简化一下主体结构,即:
^([符合要求的组成1]|[符合要求的组成2])+$
正则中有的加号 + 表示前面的内容出现一到多次进行贪婪匹配,这样会导致大量回溯,占用大量 CPU
资源,引发线上问题。
我们只需要将贪婪模式改成独占模式(加号 + 后面再加个加号 + )就可以解决这个问题,即:
^([符合要求的组成1]|[符合要求的组成2])++$
这个例子中,匹配不上时证明店铺名不合法,不需要进行回溯,因此我们可以使用独占模式,但要并不
是所有的场合都可以用独占模式解决,首先要保证正则能满足功能需求。
仔细再看一下 这个正则发现 “组成 1” 和 “组成 2” 部分中,A-Za-z 英文字母在两个集合里面重复出现
了,这会导致回溯后的重复判断,去掉重复字符也能大幅度减少正则的计算量。
这个问题将在文末的补充资料中继续详解。