正则表达式原理和优化

01. 正则匹配原理

最近工作中用到了很多正则匹配。才学习了正则表达式匹配原理以及优化。

1. 解析引擎眼中的字符串组成

对于字符串“DEF”而言,包括D、E、F三个字符和 0、1、2、3 四个数字位置:0D1E2F3,对于正则表达式而言所有源字符串,都有字符和位置。正则表达式会从0号位置,逐个去匹配的。

2. 占有字符和零宽度

正则表达式匹配过程中,如果子表达式匹配到的是字符内容,而非位置,并被保存到最终的匹配结果中,那么就认为这个子表达式是占有字符的;如果子表达式匹配的仅仅是位置,或者匹配的内容并不保存到最终的匹配结果中,那么就认为这个子表达式是零宽度的。占有字符是互斥的,零宽度是非互斥的。也就是一个字符,同一时间只能由一个子表达式匹配,而一个位置,却可以同时由多个零宽度的子表达式匹配。常见零宽字符有:^,(?=)等

3. 正则表达式匹配过程详解实例

我们掌握了上面几个概念,我们接下来分析下几个常见的解析过程。结合使用软件regexBuddy来分析。

Demo1:

源字符DEF,对应标记是:0D1E2F3,匹配正则表达式是:/DEF/

过程可以理解为:首先由正则表达式字符 /D/ 取得控制权,从位置0开始匹配,由 /D/ 来匹配“D”,匹配成功,控制权交给字符 /E/ ;由于“D”已被 /D/ 匹配,所以 /E/ 从位置1开始尝试匹配,由 /E/ 来匹配“E”,匹配成功,控制权交给 /F/ ;由 /F/ 来匹配“F”,匹配成功。

Demo2:

源字符DEF,对应标记是:0D1E2F3,匹配正则表达式是:/D\w+F/

过程可以理解为:首先由正则表达式字符 /D/ 取得控制权,从位置0开始匹配,由 /D/ 来匹配“D”,匹配成功,控制权交给字符 /\w+/ ;由于“D”已被 /D/ 匹配,所以 /\w+/ 从位置1开始尝试匹配,\w+贪婪模式,会记录一个备选状态,默认会匹配最长字符,直接匹配到EF,并且匹配成功,当前位置3了。并且把控制权交给 /F/ ;由 /F/ 匹配失败,\w+匹配会回溯一位,当前位置变成2。并把控制权交个/F/,由/F/匹配字符F成功。因此\w+这里匹配E字符,匹配完成!

参考《http://www.jb51.net/article/73403.htm》

02. 正则表达式性能优化方法

其实主要是它的“回溯”,减少“回溯”次数(减少循环查找同一个字符次数),是提高性能的主要方法。

思路:

  1. 使用正确的边界匹配器(^、$、\b、\B等),限定搜索字符串位置
  2. 尽量不适用通配符".";字符使用具体的元字符、字符类(\d、\w、\s等)(推荐)
  3. 使用正确的量词(+、*、?、{n,m}),如果能够限定长度,匹配最佳
  4. 使用非捕获组、原子组,减少没有必要的字匹配捕获用(?😃,减少内存。

附上正则表达式基础:
https://deerchao.net/tutorials/regex/regex.htm#lookaround》

还有查看分析的软件:RegexBuddy

匹配实例
http://www.jb51.net/article/85895.htm》
http://blog.csdn.net/king_xing/article/details/52771465》

03. 代码优化

Java中一般是使用Patter和Matcher进行正则匹配。
之前写的方法:

/**
 * 匹配str中的正则结果
 *
 * @param regEx 正则式String
 * @param str   待匹配的源字符串
 * @return 返回匹配的捕获组1
 */
public static String regExMatcher(String regEx, String str) {
    Pattern pattern = Pattern.compile(regEx);
    Matcher matcher = pattern.matcher(str);
    return matcher.find() ? matcher.group(1) : "";
}

上面的优化都是针对正则表达式本身的,但是其实正则的损耗还来源于Pattern.compile编译的过程。在这个过程中建立语法分析树。

因此,对于多次执行的相同pattern的匹配工具类,应该将Pattern.compile(regEx);抽取成成员变量,在类中初始化。

Ps: Pattern和String一样,是不可变类,即编译后该对象的内容就确定了,无法修改,只读的,Pattern必然是线程安全的,并发使用没有安全隐患;
在matcher()方法中也并不能对其修改

因此,改为:

/**
 * 匹配str中的正则结果
 *
 * @param regExPattern 已编译好的Pattern模式
 * @param str          待匹配的源字符串
 * @return 返回匹配的捕获组1
 */
public static String regExMatcher(Pattern regExPattern, String str) {
    Matcher matcher = regExPattern.matcher(str);
    return matcher.find() ? matcher.group(1) : "";
}
posted @ 2017-06-24 17:23  神话小小哥  阅读(3893)  评论(0编辑  收藏  举报