正则表达式性能优化的探究( 二 )


1.贪婪模式(Greedy)顾名思义 , 就是在数量匹配中 , 如果单独使用 +、?、*或(min,max)等量词 , 正则表达式会匹配尽可能多的内容 。
例如 , 上面那个例子:
text = "abbc"regex = "ab{1,3}c"就是在贪婪模式下 , NFA自动机读取了最大的匹配范围 , 即匹配 3 个 b 字符 。 匹配发生了一次失败 , 就引起了一次回溯 。 如果匹配结果是“abbbc” , 就会匹配成功 。
text = "abbbc"regex = "ab{1,3}c"2.懒惰模式(Reluctant)在该模式下 , 正则表达式会尽可能少地重复匹配字符 , 如果匹配成功 , 它会继续匹配剩余的字符串 。
例如 , 上面的例子的字符后面加一个“?” , 就可以开启懒惰模式 。
text = "abc"regex = "ab{1,3}?c"匹配结果是“abc” , 该模式下 NFA 自动机首先选择最小的匹配范围 , 即匹配 1 个 b 字符 , 因此就避免了回溯问题 。
3.独占模式(Possessive)同贪婪模式一样 , 独占模式一样会最大限度地匹配更多内容;不同的是 , 在独占模式下 , 匹配失败就会结束匹配 , 不会发生回溯问题 。
还是上面的例子 , 在字符后面加一个“+” , 就可以开启独占模式 。
text = "abbc"regex = "ab{1,3}+c"结果是不匹配 , 结束匹配 , 不会发生回溯问题 。
所以综上所述 , 避免回溯的方法就是:使用懒惰模式或独占模式 。
前面讲述了“Split() 方法使用了正则表达式实现了其强大的分割功能 , 而正则表达式的性能是非常不稳定的 , 使用不恰当会引起回溯问题 。 ” , 比如使用了 split 方法提取域名 , 并检查请求参数是否符合规定 。 split 在匹配分组时遇到特殊字符产生了大量回溯 , 解决办法就是在正则表达式后加一个需要匹配的字符和“+”解决了回溯问题:
\\?(([A-Za-z0-9-~_=%]++\\ --tt-darkmode-bgcolor: #131313;">五.正则表达式的优化1. 少用贪婪模式 :多用贪婪模式会引起回溯问题 , 可以使用独占模式来避免回溯 。
2. 减少分支选择 :分支选择类型 “(X|Y|Z)” 的正则表达式会降低性能 , 在开发的时候要尽量减少使用 。 如果一定要用 , 可以通过以下几种方式来优化:
1)考虑选择的顺序 , 将比较常用的选择项放在前面 , 使他们可以较快地被匹配;
2)可以尝试提取共用模式 , 例如 , 将 “(abcd|abef)” 替换为 “ab(cd|ef)”, 后者匹配速度较快 , 因为 NFA 自动机会尝试匹配 ab , 如果没有找到 , 就不会再尝试任何选项;
3)如果是简单的分支选择类型 , 可以用三次 index 代替 “(X|Y|Z)”, 如果测试话 , 你就会发现三次 index 的效率要比 “(X|Y|Z)” 高一些 。
3. 减少捕获嵌套 :
捕获组是指把正则表达式中 , 子表达式匹配的内容保存到以数字编号或显式命名的数组中 , 方便后面引用 。 一般一个()就是一个捕获组 , 捕获组可以进行嵌套 。
非捕获组则是指参与匹配却不进行分组编号的捕获组 , 其表达式一般由(?:exp)组成 。
在正则表达式中 , 每个捕获组都有一个编号 , 编号 0 代表整个匹配到的内容 。 可以看看下面的例子:
public static void main(String[] args) {String text = "test";String reg = "()(.*?)()";Pattern p = Pattern.compile(reg);Matcher m = p.matcher(text);while (m.find()){System.out.println(m.group(0));//整个匹配到的内容System.out.println(m.group(1));//System.out.println(m.group(2));//(.*?)System.out.println(m.group(3));//()}}=====运行结果=====testtest如果你并不需要获取某一个分组内的文本 , 那么就使用非捕获组 , 例如 , 使用 “(?:x)” 代替 “(X)”, 例如下面的例子:
public static void main(String[] args) {String text = "test";String reg = "(?:)(.*?)(?:)";Pattern p = Pattern.compile(reg);Matcher m = p.matcher(text);while (m.find()) {System.out.println(m.group(0));//整个匹配到的内容System.out.println(m.group(1));//(.*?)}}=====运行结果=====testtest作者:huangrenhui
出处: