元字符

  1. 正则表达式使用一些特定的元字符来检索、匹配和替换符合规则的字符串
  2. 元字符:普通字符、标准字符、限定字符(量词)、定位字符(边界字符)

正则表达式引擎

  1. 正则表达式是一个用正则符号写出来的公式
  • 程序对正则表达式进行语法分析,建立语法分析树
  • 再根据语法分析树结合正则表达式引擎生成执行程序(状态机),用于字符匹配
  • 正则表达式引擎是一套核心算法,用于建立状态机
  • 小结
  • 正则表达式 => 语法分析树
  • 语法分析树 + 正则表达引擎 => 状态机 => 用于字符匹配
  1. 目前实现正则表达式引擎的方式有两种
  • DFA自动机(Deterministic Finite Automaton,确定有限状态自动机)
  • NFA自动机(Nondeterministic Finite Automaton,非确定有限状态自动机)
  1. DFA自动机的构造代价远大于NFA自动机,但DFA自动机的执行效率高于NFA自动机
  • 假设一个字符串的长度为n,如果采用DFA自动机作为正则表达式引擎,则匹配的时间复杂度为O(n)
  • 如果采用NFA自动机作为正则表达式引擎,NFA自动机在匹配过程中存在大量的分支和回溯,假设NFA的状态数为s,
  • 则匹配的时间复杂度为O(ns)
  1. NFA自动机的优势是支持更多高级功能,但都是基于子表达式独立进行匹配
  • 因此在编程语言里,使用的正则表达式库都是基于NFA自动机实现的

NFA自动机

匹配过程

  1. NFA自动机会读取正则表达式的每一个字符,拿去和目标字符串匹配
  2. 匹配成功则换正则表达式的下一个字符,反之就继续就和目标字符串的下一个字符进行匹配
text="aabcab"
regex="bc"

回溯

  1. 用NFA自动机实现的比较复杂的正则表达式,在匹配过程中经常会引起回溯问题
  2. 大量的回溯会长时间占用CPU,从而带来系统性能开销
text="abbc"
regex="ab{1,3}c"

读取正则表达式第一个匹配符a和字符串第一个字符a进行比较,a对a,匹配

读取正则表达式第二个匹配符b{1,3}和字符串的第二个字符b进行比较,匹配,但b{1,3}表示1~3个字符,而NFA自动机具有贪婪特性,所以不会读取正则表达式的下一个匹配符c

使用b{1,3}和字符串的第四个字符c进行比较,发现不匹配,此时就会发生回溯,已经读取的字符串第四个字符c将被吐出去,指针回到第三个字符b的位置

发生回溯后,读取正则表达式的下一个匹配符c,和字符串的第四个字符c进行比较,结果匹配

避免回溯

避免回溯的方法:使用懒惰模式和独占模式

贪婪模式(Greedy)

  1. 在数量匹配中,如果单独使用+、?、*、{min,max}等量词,正则表达式会匹配尽可能多的内容
  2. text="abbc" , regex="ab{1,3}c",发生了一次匹配失败,就会引起一次回溯
  3. text="abbbc" , regex="ab{1,3}c",匹配成功

懒惰模式(Reluctant)

  1. 在懒惰模式下,正则表达式会尽可能少地重复匹配字符,如果匹配成功,会继续匹配剩余的字符串
  2. 使用?开启懒惰模式,text="abc" , regex="ab{1,3}?c"
  • 匹配结果是"abc",在该模式下NFA自动机首先选择最小的匹配范围,即匹配1个b字符,避免了回溯问题

独占模式(Possessive)

  1. 和贪婪模式一样,独占模式一样会最大限度地匹配更多内容,但在匹配失败时会结束匹配,不会发生回溯问题
  2. 使用+开启懒惰模式,text="abbc" , regex="ab{1,3}+bc"
  • 结果是不匹配,结束匹配,不会发生回溯问题

代码

match("ab{1,3}c", "abbc"); // abbc,贪婪模式,产生回溯
match("ab{1,3}c", "abbbc"); // abbbc,贪婪模式,不产生回溯
match("ab{1,3}?", "abbbb"); // ab,懒惰模式,不产生回溯
match("ab{1,3}+bc", "abbc"); // null,独占模式,不产生回溯

正则表达式的优化

  1. 少用贪婪模式,多用独占模式(避免回溯)
  2. 减少分支选择,分支选择类型"(X|Y|Z)"的正则表达式会降低性能,尽量减少使用,如果一定要使用
  • 考虑选择的顺序,将比较常用的选择放在前面,使它们可以较快地被匹配
  • 提取共用模式,(abcd|abef) => ab(cd|ef)
  • 如果是简单的分支选择类型,可以用三次index代替(X|Y|Z)
  1. 减少捕获嵌套
  • 捕获组:把正则表达式中,子表达式匹配的内容保存到以数字编号或显式命名的数组中,一般一个()就是一个捕获组
  • 每个捕获组都有一个编号,编号0代表整个匹配到的内容
  • 非捕获组:参与匹配却不进行分组编号的捕获组,其表达式一般由(?:exp)组成
  • 减少不需要获取的分组,可以提高正则表达式的性能

捕获组

String text = "<input high=\"20\" weight=\"70\">test</input>";
String reg = "(<input.*?>)(.*?)(</input>)";
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));//(<input.*?>)
System.out.println(m.group(2));//(.*?)
System.out.println(m.group(3));//(</input>)
// 输出:
// <input high="20" weight="70">test</input>
// <input high="20" weight="70">
// test
// </input>
}

非捕获组

String text = "<input high=\"20\" weight=\"70\">test</input>";
String reg = "(?:<input.*?>)(.*?)(?:</input>)";
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));//(.*?)
// 输出
// <input high="20" weight="70">test</input>
// test
}

小结

在做好性能测试的前提下,可以使用正则表达式,否则能不用就不用,避免造成更多的性能问题.

文章的话到这里就结束了,希望大家在性能测试中,对正则表达式有自己的认识。今日的性能篇到此结束!

需要更多源码视频,面试题,Java技术书籍等学习资料的

可以关注我哦!加群772300343即可获取!

我是小架,我们下篇文章见!

Java正则表达式详细解析的更多相关文章

  1. java正则表达式详细总结

    Java 提供了功能强大的正则表达式API,在java.util.regex 包下.本教程介绍如何使用正则表达式API. 正则表达式 一个正则表达式是一个用于文本搜索的文本模式.换句话说,在文本中搜索 ...

  2. Java 正则表达式详细使用

    Java 正则表达式 java.util.regex.Pattern java.util.regex.Matcher 1.Matchmatch 是从字符串最头部开始匹配,一直到结束,需要匹配整个串 S ...

  3. Java 正则表达式详细实例解析

    案例1:判断字符串是否是abc package Regex; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * ...

  4. java容器详细解析

    前言:在java开发中我们肯定会大量的使用集合,在这里我将总结常见的集合类,每个集合类的优点和缺点,以便我们能更好的使用集合.下面我用一幅图来表示 其中淡绿色的表示接口,红色的表示我们经常使用的类. ...

  5. WeakHashMap和Java引用类型详细解析

    WeakHashMap是种弱引用的HashMap,这是说,WeakHashMap里的key值如果没有外部强引用,在垃圾回收之后,WeakHashMap的对应内容也会被移除掉. 1.1 Java的引用类 ...

  6. java容器详细解析(转)

    :在java开发中我们肯定会大量的使用集合,在这里我将总结常见的集合类,每个集合类的优点和缺点,以便我们能更好的使用集合.下面我用一幅图来表示 其中淡绿色的表示接口,红色的表示我们经常使用的类. 1: ...

  7. JAVA 正则表达式 (超详细)

    (PS:这篇文章为转载,我不喜欢转载的但我觉得这篇文章实在是超赞了,就转了过来,这篇可以说是学习JAVA正则表达的必读篇.作者是个正真有功力的人,阅读愉快) 在Sun的Java JDK 1.40版本中 ...

  8. 转载:JAVA 正则表达式 (超详细)

    在Sun的JavaJDK 1.40版本中,Java自带了支持正则表达式的包,本文就抛砖引玉地介绍了如何使用Java.util.regex包. 可粗略估计一下,除了偶尔用Linux的外,其他Linu x ...

  9. java类生命周期详细解析

    (一)详解java类的生命周期 引言 最近有位细心的朋友在阅读笔者的文章时,对java类的生命周期问题有一些疑惑,笔者打开百度搜了一下相关的问题,看到网上的资料很少有把这个问题讲明白的,主要是因为目前 ...

随机推荐

  1. Java方法之参数传递机制

    目录 Java方法之参数传递机制 基本数据类型 引用数据类型 综合练习 总结 Java方法之参数传递机制 Java方法中如果声明了形参,在调用方法时就必须给这些形参指定参数值,实际传进去的这个值就叫做 ...

  2. [Spring cloud 一步步实现广告系统] 10. 使用Ribbon 实现微服务调用

    在使用Ribbon调用广告投放系统API之前,我们需要先创建2个VO对象,AdPlanVO,AdPlanGetRequestVO. //数据请求对象 @Data @NoArgsConstructor ...

  3. C#函数的递归

    using System; namespace ConsoleApp3 { class Program { static void Main(string[] args) { ); Console.W ...

  4. ASP.NET MVC5基础-控制器(Controller)详解

    在上文ASP.NET MVC5基础 – MVC文件架构中我们简单了解了下控制器Controller的作用,本文我将详细介绍控制器Controller的使用方法. Controller的运行过程 上文我 ...

  5. 项目部署到Linux上遇到的坑

    作者:晨钟暮鼓c个人微信公众号:程序猿的月光宝盒 1.本地Navicat for MySQL无法连接至服务器(Centos 7 x86_64 bbr) 1045错误: 解决步骤: ​ 1.查看用户名密 ...

  6. 松软科技web课堂:SQLServer之LEN() 函数

    LEN() 函数 LEN 函数返回文本字段中值的长度. SQL LEN() 语法 SELECT LEN(column_name) FROM table_name SQL LEN() 实例 我们拥有下面 ...

  7. 射频IC卡和IC卡读卡器的成本分析

    当今射频IC卡和IC卡读卡器的种类繁多,很多人问IC卡读卡器多少钱,那么如何在满足我们需求的情况下最大的节省成本呢.下面就各种射频IC卡和IC卡读卡器来分析下各自的成本.                ...

  8. R-2 - 正态分布-中心极限-置信区间-正态假设检验

    本节内容 1:样本估计总体均值跟标准差,以及标准误 2:中心极限定理 3:如何查看数据是否是正态分布QQ图 4:置信区间的理解跟案例 5:假设检验 参考文章: 假设检验的学习和理解 一.样本估计总体均 ...

  9. matlab练习程序(螺线拟合)

    这里待拟合的螺线我们选择阿基米德螺线,对数螺线类似. 螺线的笛卡尔坐标系方程为:   螺线从笛卡尔坐标转为极坐标方程为:   阿基米德螺线在极坐标系下极径r和极角theta为线性关系,方程为:   计 ...

  10. Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3) F2. Wrong Answer on test 233 (Hard Version) dp 数学

    F2. Wrong Answer on test 233 (Hard Version) Your program fails again. This time it gets "Wrong ...