使用正则表达式时-可能会导致性能下降的情况

目录

前言

正则表达式引擎

NFA自动机的回溯

解决方案


  • 前言

  • 正则表达式是一个用正则符号写出的公式,程序对这个公式进行语法分析,建立一个语法分析树,再根据这个分析树结合正则表达式的引擎生成执行程序(这个执行程序我们把它称作状态机,也叫状态自动机),用于字符匹配
  • 使用不恰当的正则表达式可能会导致很严重的性能问题
  • 比如:

  • 这个正则表达式看起来没什么问题,它可以分为三个部分:
  • 第一部分匹配 http 和 https 协议,第二部分匹配 www. 字符,第三部分匹配许多字符
  • 其实这里会导致 CPU 使用率高的关键原因就是:Java 正则表达式使用的引擎实现是 NFA 自动机,这种正则表达式引擎在进行字符匹配时会发生回溯backtracking
  • 而一旦发生回溯,那其消耗的时间就会变得很长,有可能是几分钟,也有可能是几个小时,时间长短取决于回溯的次数和复杂度
  • 正则表达式引擎

  • 正则表达式引擎就是一套核心算法,用于建立状态机
  • 简单地说,实现正则表达式引擎的有两种方式:DFA 自动机(Deterministic Final Automata 确定型有穷自动机)和 NFA 自动机(Non deterministic Finite Automaton 不确定型有穷自动机)
  • 简单来讲,NFA 对应的是正则表达式主导的匹配,而 DFA 对应的是文本主导的匹配
  • 简单来讲,DFA 自动机的时间复杂度是线性的,更加稳定,但是功能有限;而 NFA 的时间复杂度比较不稳定,有时候很好,有时候不怎么好,好不好取决于你写的正则表达式;但是胜在 NFA 的功能更加强大,所以包括 Java、.NET、Perl、Python、Ruby、PHP 等语言都使用了 NFA 去实现其正则表达式
  • 那 NFA 自动机到底是怎么进行匹配的呢?我们以下面的字符和表达式来举例说明:

  • 要记住一个很重要的点,即:NFA 是以正则表达式为基准去匹配的
  • 也就是说,NFA 自动机会读取正则表达式的一个一个字符,然后拿去和目标字符串匹配,匹配成功就换正则表达式的下一个字符,否则继续和目标字符串的下一个字符比较
    • 首先,拿到正则表达式的第一个匹配符:d
    • 于是拿去和字符串的字符进行比较,字符串的第一个字符是T,不匹配,换下一个
    • 第二个是o,也不匹配,再换下一个
    • 第三个是d,匹配了,那么就读取正则表达式的第二个字符:a
    • 读取到正则表达式的第二个匹配符:a
    • 拿着继续和字符串的第四个字符 a 比较,又匹配了
    • 那么接着读取正则表达式的第三个字符:y
    • 读取到正则表达式的第三个匹配符:y
    • 拿着继续和字符串的第五个字符 y 比较,又匹配了
    • 尝试读取正则表达式的下一个字符,发现没有了,那么匹配结束
  • 上面这个匹配过程就是 NFA 自动机的匹配过程,但实际上的匹配过程会比这个复杂非常多,但其原理是不变的
  • NFA自动机的回溯

  • 了解了 NFA 是如何进行字符串匹配的,接下来我们就可以讲讲这篇文章的重点了:回溯
  • 为了更好地解释回溯,我们同样以下面的例子来讲解:

  • 上面的这个例子的目的比较简单,匹配以 a 开头,以 c 结尾,中间有 1-3 个 b 字符的字符串
  • NFA 对其解析的过程是这样子的:
    • 首先,读取正则表达式第一个匹配符 a 和 字符串第一个字符 a 比较,匹配了
    • 于是读取正则表达式第二个字符
    • 读取正则表达式第二个匹配符 b{1,3} 和字符串的第二个字符 b 比较,匹配了
    • 但因为 b{1,3} 表示 1-3 个 b 字符串,以及 NFA 自动机的贪婪特性(也就是说要尽可能多地匹配),所以此时并不会再去读取下一个正则表达式的匹配符,而是依旧使用 b{1,3} 和字符串的第三个字符 b 比较,发现还是匹配
    • 于是继续使用 b{1,3} 和字符串的第四个字符 c 比较,发现不匹配了
    • 此时就会发生回溯
    • 发生回溯是怎么操作呢?
    • 发生回溯后,我们已经读取的字符串第四个字符 c 将被吐出去,指针回到第三个字符串的位置
    • 之后,程序读取正则表达式的下一个操作符 c,读取当前指针的下一个字符 c 进行对比,发现匹配
    • 于是读取下一个操作符,但这里已经结束了
  • 下面我们回过头来看看前面的那个校验 URL 的正则表达式:

  • 出现问题的 URL 是:

  • 我们把这个正则表达式分为三个部分:
    • 第一部分:校验协议;^([hH][tT]{2}[pP]://|[hH][tT]{2}[pP][sS]://)
    • 第二部分:校验域名;(([A-Za-z0-9-~]+).)+
    • 第三部分:校验参数;([A-Za-z0-9-~\\/])+$
  • 我们可以发现正则表达式校验协议 http:// 这部分是没有问题的,但是在校验 www.fapiao.com 的时候,其使用了 xxxx. 这种方式去校验
  • 那么匹配过程是这样的:
    • 匹配到 www.
    • 匹配到 fapiao.
    • 匹配到 com/dzfp-web/pdf/download?request=6e7JGm38jf.....,你会发现因为贪婪匹配的原因,所以程序会一直读后面的字符串进行匹配,最后发现没有点号,于是就一个个字符回溯回去了
  • 这是这个正则表达式存在的第一个问题
  • 另外一个问题是在正则表达式的第三部分,我们发现出现问题的 URL 是有下划线(_)和百分号(%)的,但是对应第三部分的正则表达式里面却没有
  • 这样就会导致前面匹配了一长串的字符之后,发现不匹配,最后回溯回去
  • 这是这个正则表达式存在的第二个问题
  • 解决方案

  • 明白了回溯是导致问题的原因之后,其实就是减少这种回溯,你会发现如果我在第三部分加上下划线和百分号之后,程序就正常了

  • 运行上面的程序,立刻就会打印出match!!
  • 但这是不够的,如果以后还有其他 URL 包含了乱七八糟的字符呢,我们难不成还再修改一遍
  • 肯定不现实
  • 其实在正则表达式中有这么三种模式:贪婪模式、懒惰模式、独占模式
  • 在关于数量的匹配中,有 + ? * {min,max} 四种两次,如果只是单独使用,那么它们就是贪婪模式
  • 如果在他们之后多加一个 ? 符号,那么原先的贪婪模式就会变成懒惰模式,即尽可能少地匹配
  • 但是懒惰模式还是会发生回溯现象
  • 例如下面这个例子:

  • 正则表达式的第一个操作符 a 与 字符串第一个字符 a 匹配,匹配成功
  • 于是正则表达式的第二个操作符 b{1,3}? 和 字符串第二个字符 b 匹配,匹配成功
  • 因为最小匹配原则,所以拿正则表达式第三个操作符 c 与字符串第三个字符 b 匹配,发现不匹配
  • 于是回溯回去,拿正则表达式第二个操作符 b{1,3}? 和字符串第三个字符 b 匹配,匹配成功
  • 于是再拿正则表达式第三个操作符 c 与字符串第四个字符 c 匹配,匹配成功;于是结束
  • 如果在他们之后多加一个 + 符号,那么原先的贪婪模式就会变成独占模式,即尽可能多地匹配,但是不回溯
  • 于是乎,如果要彻底解决问题,就要在保证功能的同时确保不发生回溯
  • 将上面校验 URL 的正则表达式的第二部分后面加多了个 + 号,即变成这样:

  • 这样之后,运行原有的程序就没有问题了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/219056.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

6、原型模式(Prototype Pattern,不常用)

原型模式指通过调用原型实例的Clone方法或其他手段来创建对象。 原型模式属于创建型设计模式,它以当前对象为原型(蓝本)来创建另一个新的对象,而无须知道创建的细节。原型模式在Java中通常使用Clone技术实现,在JavaSc…

参加百度Apollo技术沙龙—感受自动驾驶的魅力

2023年12月2日下午2点,我有幸参加了百度Apollo技术沙龙,这是一个围绕Apollo新版本Beta的全面升级展开的深度交流活动。作为一名工程师,我深感荣幸能够与众多同行和专家一同探讨自动驾驶技术的快速发展 在这次沙龙中,我了解到Apo…

可编程电子负载原理是怎样的

可编程电子负载是一种模拟真实负载的电子设备,它可以模拟各种不同类型和规格的负载,如电阻、电容、电感等。通过调整电子负载的参数,可以实现对电源输出电压、电流、功率等性能指标的精确控制。可编程电子负载广泛应用于电源测试、电池充放电…

基于vue+node.js智慧校园学生办证系统

基于vuenode.js智慧校园学生办证系统 摘要:随着计算机技术和网络技术的飞快发展,它加速了国内信息化建设的进程,信息技术对管理改革产生了深远的影响。为了适应新时代的发展趋势,各行各业都高度重视信息化建设。在教育领域&#…

成为Java开发高手:掌握Spring框架的关键技能-DI

DI相关内容 1.1 setter注入1.1.2 注入引用数据类型1.1.3 注入简单数据类型步骤1:声明属性并提供setter方法步骤2:配置文件中进行注入配置步骤3:运行程序 1.2 构造器注入1.2.2 构造器注入引用数据类型步骤1:删除setter方法并提供构造方法步骤2:配置文件中进行配置构造方式注入步…

4.9 构建onnx结构模型-Equal

前言 构建onnx方式通常有两种: 1、通过代码转换成onnx结构,比如pytorch —> onnx 2、通过onnx 自定义结点,图,生成onnx结构 本文主要是简单学习和使用两种不同onnx结构, 下面以 Equal 结点进行分析 方式 方法一…

北京华联BHGMall“宠粉模式”不断迭代,强体验注互动成行业UP主

在今年双11热度遇冷后,双十二被官宣取消,而这背后本质已经间接印证:传统“电商大促”的模式,已经难以为继。反观线下消费市场,则是以持续恢复和增长成为经济恢复的亮点,从线下客流量的快速回升,…

软考2016年上半年第六题(适配器模式)与手术训练系统项目适配器模式的应用

软考2016年上半年第六题 public class Address {public void street(){System.out.println("a");};public void zip(){};public void city(){}; }package org.example.适配器模式;/*** 适配器模式(Adapter Pattern)是作为两个不兼容的接口之间…

整数和浮点数在内存中的存储​(大小端详解)

目录 一、整数在内存中的存储 二、大小端字节序和字节序判断 2.1为什么有大小端?​ 2.2请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。(10分)-百度笔试题 方法一(char*强制类型转换&#xff09…

疑难杂症 之 关闭模态窗口之后刷新父窗口

疑难杂症 之 关闭模态窗口之后刷新父窗口 1. 模态窗口 与 非模态窗口2. 弹出模态窗口2.1 实现效果2.2 实现代码2.2.1 刷新父窗口2.2.2 完整代码 2.3 参考 3. 其他刷新父窗口(模态窗口页面与父窗口不在同一页面)3.1 实现代码3.1.1 核心代码3.1.2 多层模态…

什么是Amazon Lambda(无服务器计算服务)

Lambda 在高可用性计算基础设施上运行代码,用于执行计算资源的所有管理工作。这包括服务器和操作系统维护、容量调配和弹性伸缩、代码和安全补丁部署以及代码监控和日志记录。您只需要提供代码。 最近亚马逊云服务提供了超多免费的云服务,快来领取免费套…

java版微信小程序商城免费搭建 java版直播商城平台规划及常见的营销模式有哪些?电商源码/小程序/三级分销

涉及平台 平台管理、商家端(PC端、手机端)、买家平台(H5/公众号、小程序、APP端(IOS/Android)、微服务平台(业务服务) 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis …

利用 FormData 实现文件上传、监控网路速度和上传进度(前端原生,后端 koa)

利用 FormData 实现文件上传 基础功能:上传文件 演示如下: 概括流程: 前端:把文件数据获取并 append 到 FormData 对象中后端:通过 ctx.request.files 对象拿到二进制数据,获得 node 暂存的文件路径 前端…

学习Opencv(蝴蝶书/C++)——4.图形和大型数组类型(上)

文章目录 1. cv::Mat类的成员变量1.1 flags1.2 cv::Mat::step2 存储方式,存储位置计算2.1 存储方式2.2 🌈存储位置计算2.2.1 基本计算公式2.2.1 step代码说明2.2.3 内存地址计算代码说明3 创建数据3.0 Mat的构成3.0.1 3.0版本之后的Mat3.0.2 cvMat3.1 构造函数3.2 🌈构造函…

【MySQL】MySQL数据库基础

MySQL数据库基础 一、为什么要有数据库?二、 数据库软件的构成数据库服务器,数据库,表关系主流数据库 三、基本使用1、连接服务器2、服务器管理3、MySQL配置文件4、数据库的简单操作5、数据逻辑存储 四、MySQL架构SQL分类MySQL客户端存储引擎…

Hadoop学习笔记(HDP)-Part.10 创建集群

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …

[ 蓝桥杯Web真题 ]-外卖给好评

目录 介绍 准备 目标 效果 规定 思路 解答参考 介绍 外卖是现代生活中必备的一环。收到外卖后,各大平台软件常常会邀请用户在口味,配送速度等多个方面给与评分。在 element-ui 组件中,已经有相应的 Rate 组件,但是已有组件…

vue3 vue-router过渡动效 滚动行为 (四)

文章目录 一、过渡动效1.1安装animate.css1.2 利用元信息存储过渡名称1.3 在组件中使用 二、滚动行为2.1 始终滚动到顶部2.2 相对于某个元素的偏移量2.3 保持之前的滚动位置 一、过渡动效 1.1安装animate.css npm install animate.css --save1.2 利用元信息存储过渡名称 {pa…

编译原理:NFA转DFA(原理+完整代码+可视化实现)

NFA转换为DFA 【本文内容摘要】 什么是DFA通过子集构造法将NFA转换为DFA生成DFA的dot文件并且形成可视化。 如果本文对各位看官有用的话,请记得给一个免费的赞哦(收藏也不错)! 文章目录 NFA转换为DFA一、什么是DFA二、NFA转换为…