文章来自微信公众号:前端工坊(fe_workshop),不定期更新有趣好玩的前端相关原创技术文章。喜欢的话请关注公众号:前端工坊版权归公众号所有,转载请注明出处。作者:京东商城-成都研究院-JSHOP研发部陆星源正则表达式-从模糊到清晰查找特定信息(Search),或查找并编辑特定信息(Replace)。它是一种内置于其他语言(例如Javscript、Java等)中的“迷你”语言。2.需要承认的事实是,常规答案并不是唯一的。几乎每个问题都有不止一个解决方案。有的更简单,有的更快,有的兼容性更好,有的功能更多。我们需要根据我们的需求来确定最适合我们的解决方案。3.正则化引擎概述正则化引擎可以分为两类。一种称为NFA(非确定性有限自动机),另一种称为DFA(确定性有限自动机)。好吧,概念不好理解,举个例子:正则:to(Jack|Rose|Jerry)匹配文本:xxx···toJerry1)NFA(expression-led)匹配过程正则表达式从第一个t开始在正则中,每次正则引擎查看表达式的一部分,同时检查当前文本是否与表达式的当前部分匹配。如果是,则继续表达式的下一部分,如果是,直到表达式的所有部分都匹配。这时发现在检查当前文本中的字符t时,正则表达式的第一项匹配成功,然后会检查紧跟其后的字符是否可以被o匹配,然后查找可以,然后检查后面的元素,此时后面的元素是(Jack|Rose|Jerry),引擎会尝试3种可能分别测试,直到匹配成功。2)当DFA(text-driven)匹配流程引擎扫描到当前文本时,会记录下所有当前有效的匹配可能性。当引擎移动到文本中的t时,它会为当前处理的匹配可能性添加一个潜在的可能性:对于接下来扫描的每个字符,更新当前可能匹配的序列。例如扫码到J匹配到文本时,有效可能匹配变为2,Rose被淘汰。当扫描到与文本匹配的e时,Jack也被淘汰,只留下一个可能的匹配项。当后面的rry匹配完成后,整个匹配就完成了。3)两句评论NFA和DFA1,DFA匹配速度快但特征少(比如不支持capturegroups和backreferences),NFA匹配速度较慢但功能强大;2、DFA就像装了电动引擎的汽车,加速很快很快,但是续航时间短,不能跑远,而NFA可以看成是汽油引擎汽车。加速不是那么快,但是适应性广,可以去任何地方,但是因为适应性广,所以调教就很重要了。4)注意Java、Javascript、PHP、Python都是NFA引擎。4.基础过头(老手略过)5.重点讲解1)贪心和懒惰贪心模式:尽可能匹配更多的字符。举个栗子:Regular:
.*
结果:从匹配过程中我们也可以发现,对于.*这个表达式会尝试匹配尽可能多的字符,直到匹配结束,它将尝试匹配常规的结尾。惰性模式:与贪婪模式相反,匹配尽可能少的字符。举个栗子:Regularity:
.*?
结果:从匹配过程中我们也可以发现,正则末尾的会被优先匹配,作为如果这个结局不令人满意,则尽可能多。少匹配.*?表达。2)子表达式和反向引用子表达式:考虑这种场景,虽然有些短语是由多个词组成的,但它们实际上是一个整体,需要作为一个独立的元素来使用。在这种情况下,需要使用子表达式来表达。子表达式必须括在()括号中。目的是精确设置需要重复的文字和重复的次数。反向引用:它允许我们在正则表达式中引用前一个子表达式匹配的结果。什么用途?或者举个栗子:要求:匹配Html代码片段中的h1~h6标签Regularity:
.*?(nobackreferencesused)结果:Regularity:
.*?(使用后向引用)结果:3)回溯NFA引擎匹配能力强,但调优不当可能导致性能问题。它还有一个叫做回溯失控。那么问题来了,什么是回溯?举个栗子:当我们醒来的时候,突然发现自己被困在了一个山洞里。这时,我们需要寻找出路,但前方有岔路口。这个时候是没有依据告诉我们哪条路是出路的,只能一条一条的去试,所以我们可以标记一下岔路口,这样如果我们选择的路走不通,我们就可以走原路返回,直到我们遇到标记的岔路口,所以继续尝试另一条路不是出路。我们可以将每次尝试寻找之前标记的地方失败后返回的过程称为回溯。在许多情况下,根据您编写的正则表达式,正则引擎或多或少需要在两个或多个选项之间进行选择。4)断言(环顾四周)先不解释技术名词,先来看这样一个应用场景的需求:匹配网页PC商品详情页地址格式中所有PC商品详情页地址包含的sku信息://item.jd.com/xxxxxx.html方法一:先正则匹配,再截断固定.html正则:/d+\.html/g方法二:先正则匹配,再截断固定字符正则:/item\.jd\.com/d+\.html/g方法三:使用正向断言和反向断言保证准确性,只返回sku号正则:/(?<=item\.jd\.com/)d+(?=\.html)/g断言分类:positivepositiveassertion,forwardnegativeassertion,reversepositiveassertion,reversenegativeassertion注:Javascript不支持反向断言,Java对反向断言的支持也有限。简而言之,我们在匹配目标关键字的时候,也期望对目标关键字的前后进行限制,而我们并不期望在匹配结果中出现这些限制。这时候可以使用断言。6.正则优化1)什么是好的正则?精度:只匹配预期的文字,排除不需要的文字要求:匹配jshop手机活动页面url的域名部分jshop手机活动页面URL格式://xxxx.jd.xxx/m/act/xxxxxx.html规则:///(.*)(?=/m)/g规则:///(1*)/g注释:如果不需要匹配/,应该在正则表达式中规定匹配效率:匹配结果会迅速归还。如果匹配结果无法匹配,将在最短的时间内报告匹配失败。太多的选择分支很容易成为效率杀手,因为任何匹配失败的选择分支都会导致回溯。所以提高正则匹配效率的方法之一就是减少多选分支。举个栗子:需求:匹配用户输入的字符串是否为4位IP中的bit。说白了就是匹配0~255。分析:可能有1位,或2位,或3位位。当有3位时,需要单独判断。当第一位为0或1时,接下来的两位可以是任意数字。当第一位为2时,第二位只能为0-5。而当第二位为0-4时,第三位可以为任意数,但当第二位为5时,第三位只能为0-5。翻译正则:/d|dd|[01]dd|2[0-4]d|25[0-5]/合并相似项后:/[01]?dd?|2[0-4]d|25[0-5]/评论:通过合并相似项可以减少多选分支。还有第一个多项选择分支使用dd?://jex.im/regulex可以实现对复杂整条表达式的正则表达式性能测试。例如猫头鹰工具RegexBuddy可以用来测试正则表达式的匹配过程和性能,包括对各种语言正则特性的支持。.3)优化方法优化策略:减少回溯1、减少或合并多选分支2、避免量词嵌套3、占用优先量词。可以减少回溯,可惜js不支持,java支持。举个栗子:考虑到/a+b/和/a++b/这两个正则,被测字符串aaaa/a+b/的匹配过程就是/a++b/的匹配过程4.正确使用boundaryMatchers(^、$、b、B等),限制搜索字符串位置5,尽量不要使用通配符“.”;字符使用特定的元字符、字符类(d、w、s等)(推荐)6、使用正确的量词(+、*、?、{n,m}),如果可以限制长度,匹配最好7、使用非捕获括号。如果不需要引用括号内的文字,请使用非捕获括号(?:...),好处是节省捕获时间,减少回溯使用的状态数。/?