正则表达式之 NFA 引擎匹配原理详解

文章目录

  • 一、为什么要了解引擎匹配原理
  • 二、正则表达式引擎
  • 三、预备知识
    • (一)字符串组成
    • (二)占有字符和零宽度
    • (三)控制权和传动
  • 四、正则表达式简单匹本过程
    • (一)基础匹配过程
    • (二)含有匹配优先量词的匹配过程
      • 匹配成功一
      • 匹配成功二
      • 匹配失败
    • (三)含有忽略优先量词的匹配过程
      • 匹配成功
    • (四)零宽度匹配过程

一、为什么要了解引擎匹配原理

一个个音符杂乱无章的组合在一起,弹奏出的或许就是噪音,同样的音符经过作曲家的手,就可以谱出非常动听的乐曲,一个演奏者同样可以照着乐谱奏出动听的乐曲,但他/她或许不知道该如何去改变音符的组合,使得乐曲更动听。

作为正则的使用者也一样,不懂正则引擎原理的情况下,同样可以写出满足需求的正则,但是不知道原理,却很难写出高效且没有隐患的正则。所以对于经常使用正则,或是有兴趣深入学习正则的人,还是有必要了解一下正则引擎的匹配原理的。

二、正则表达式引擎

正则引擎大体上可分为不同的两类:DFANFA,而 NFA 又基本上可以分为传统型 NFAPOSIX NFA

  1. DFA(Deterministic Finite Automaton) 确定型有穷自动机

  2. NFA(Non-deterministic finite automaton) 非确定型有穷自动机
    ① Traditional NFA
    ② POSIX NFA

DFA 引擎因为不需要回溯,所以匹配快速,但不支持捕获组,所以也就不支持反向引用和 $number 这种引用方式,目前使用 DFA 引擎的语言和工具主要有 awk、egrep 和 lex。

POSIX NFA 主要指符合 POSIX 标准的NFA引擎,它的特点主要是提供 longest-leftmost 匹配,也就是在找到最左侧最长匹配之前,它将继续回溯。同 DFA 一样,非贪婪模式或者说忽略优先量词对于 POSIX NFA 同样是没有意义的。

大多数语言和工具使用的是传统型的 NFA 引擎,它有一些 DFA 不支持的特性:

  1. 捕获组、反向引用和$number引用方式;

  2. 环视(Lookaround,(?<=…)、(?<!…)、(?=…)、(?!…)),或者有的有文章叫做预搜索;

  3. 忽略优化量词(??、*?、+?、{m,n}?、{m,}?),或者有的文章叫做非贪婪模式;

  4. 占有优先量词(?+、*+、++、{m,n}+、{m,}+,目前仅 Java 和 PCRE 支持),固化分组(?>…)

引擎间的区别不是本文的重点,仅做简要的介绍,有兴趣的可参考相关文献。

三、预备知识

(一)字符串组成

在这里插入图片描述
对于字符串“abc”而言,包括三个字符和四个位置。

(二)占有字符和零宽度

正则表达式匹配过程中,如果子表达式匹配到的是字符内容,而非位置,并被保存到最终的匹配结果中,那么就认为这个子表达式是占有字符的;如果子表达式匹配的仅仅是位置,或者匹配的内容并不保存到最终的匹配结果中,那么就认为这个子表达式是零宽度的。

占有字符是互斥的,零宽度是非互斥的。也就是一个字符,同一时间只能由一个子表达式匹配,而一个位置,却可以同时由多个零宽度的子表达式匹配。

(三)控制权和传动

正则的匹配过程,通常情况下都是由一个子表达式(可能为一个普通字符、元字符或元字符序列组成)取得控制权,从字符串的某一位置开始尝试匹配,一个子表达式开始尝试匹配的位置,是从前一子表达匹配成功的结束位置开始的。如正则表达式:

(子表达式一)(子表达式二)

假设(子表达式一)为零宽度表达式,由于它匹配开始和结束的位置是同一个,如位置 0,那么(子表达式二)是从位置 0 开始尝试匹配的。

假设(子表达式一)为占有字符的表达式,由于它匹配开始和结束的位置不是同一个,如匹配成功开始于位置 0,结束于位置 2,那么(子表达式二)是从位置 2 开始尝试匹配的。

而对于整个表达式来说,通常是由字符串位置 0 开始尝试匹配的。如果在位置 0 开始的尝试,匹配到字符串某一位置时整个表达式匹配失败,那么引擎会使正则向前传动,整个表达式从位置1开始重新尝试匹配,依此类推,直到报告匹配成功或尝试到最后一个位置后报告匹配失败。

四、正则表达式简单匹本过程

(一)基础匹配过程

在这里插入图片描述
源字符串:abc

正则表达式:abc

匹配过程:

首先由字符“a”取得控制权,从位置 0 开始匹配,由“a”来匹配“a”,匹配成功,控制权交给字符“b”;由于“a”已被“a”匹配,所以“b”从位置 1 开始尝试匹配,由“b”来匹配“b”,匹配成功,控制权交给“c”;由“c”来匹配“c”,匹配成功。

此时正则表达式匹配完成,报告匹配成功。匹配结果为“abc”,开始位置为 0,结束位置为 3。

(二)含有匹配优先量词的匹配过程

匹配成功一

在这里插入图片描述
源字符串:abc

正则表达式:ab?c

量词“?”属于匹配优先量词,在可匹配可不匹配时,会先选择尝试匹配,只有这种选择会使整个表达式无法匹配成功时,才会尝试让出匹配到的内容。这里的量词“?”是用来修饰字符“b”的,所以“b?”是一个整体。

匹配过程:

首先由字符“a”取得控制权,从位置 0 开始匹配,由“a”来匹配“a”,匹配成功,控制权交给字符“b?”;由于“?”是匹配优先量词,所以会先尝试进行匹配,由“b?”来匹配“b”,匹配成功,控制权交给“c”,同时记录一个备选状态;由“c”来匹配“c”,匹配成功。记录的备选状态丢弃。

此时正则表达式匹配完成,报告匹配成功。匹配结果为“abc”,开始位置为 0,结束位置为 3。

匹配成功二

在这里插入图片描述
源字符串:ac

正则表达式:ab?c

匹配过程:

首先由字符“a”取得控制权,从位置 0 开始匹配,由“a”来匹配“a”,匹配成功,控制权交给字符“b?”;先尝试进行匹配,由“b?”来匹配“c”,同时记录一个备选状态,匹配失败,此时进行回溯,找到备选状态,“b?”忽略匹配,让出控制权,把控制权交给“c”;由“c”来匹配“c”,匹配成功。

此时正则表达式匹配完成,报告匹配成功。匹配结果为“ac”,开始位置为 0,结束位置为 2。其中“b?”不匹配任何内容。

匹配失败

在这里插入图片描述
源字符串:abd

正则表达式:ab?c

匹配过程:

首先由字符“a”取得控制权,从位置 0 开始匹配,由“a”来匹配“a”,匹配成功,控制权交给字符“b?”;先尝试进行匹配,由“b?”来匹配“b”,同时记录一个备选状态,匹配成功,控制权交给“c”;由“c”来匹配“d”,匹配失败,此时进行回溯,找到记录的备选状态,“b?”忽略匹配,即“b?”不匹配“b”,让出控制权,把控制权交给“c”;由“c”来匹配“b”,匹配失败。此时第一轮匹配尝试失败。

正则引擎使正则向前传动,由位置 1 开始尝试匹配,由“a”来匹配“b”,匹配失败,没有备选状态,第二轮匹配尝试失败。

继续向前传动,直到在位置 3 尝试匹配失败,匹配结束。此时报告整个表达式匹配失败。

(三)含有忽略优先量词的匹配过程

匹配成功

在这里插入图片描述
源字符串:abc

正则表达式:ab??c

量词“??”属于忽略优先量词,在可匹配可不匹配时,会先选择不匹配,只有这种选择会使整个表达式无法匹配成功时,才会尝试进行匹配。这里的量词“??”是用来修饰字符“b”的,所以“b??”是一个整体。

匹配过程:

首先由字符“a”取得控制权,从位置 0 开始匹配,由“a”来匹配“a”,匹配成功,控制权交给字符“b??”;先尝试忽略匹配,即“b??”不进行匹配,同时记录一个备选状态,控制权交给“c”;由“c”来匹配“b”,匹配失败,此时进行回溯,找到记录的备选状态,“b??”尝试匹配,即“b??”来匹配“b”,匹配成功,把控制权交给“c”;由“c”来匹配“c”,匹配成功。

此时正则表达式匹配完成,报告匹配成功。匹配结果为“abc”,开始位置为 0,结束位置为 3。其中“b??”匹配字符“b”。

(四)零宽度匹配过程

在这里插入图片描述
源字符串:a12

正则表达式:^(?=[a-z])[a-z0-9]+$

元字符“^”和“$”匹配的只是位置,顺序环视“(?=[a-z])”只进行匹配,并不占有字符,也不将匹配的内容保存到最终的匹配结果,所以都是零宽度的。

这个正则的意义就是匹配由字母或数字组成的,第一个字符是字母的字符串。

匹配过程:

首先由元字符“^”取得控制权,从位置 0 开始匹配,“^”匹配的就是开始位置“位置 0”,匹配成功,控制权交给顺序环视“(?=[a-z])”;

(?=[a-z])”要求它所在位置右侧必须是字母才能匹配成功,零宽度的子表达式之间是不互斥的,即同一个位置可以同时由多个零宽度子表达式匹配,所以它也是从位置 0 尝试进行匹配,位置 0 的右侧是字符“a”,符合要求,匹配成功,控制权交给“[a-z0-9]+”;

因为“(?=[a-z])”只进行匹配,并不将匹配到的内容保存到最后结果,并且“(?=[a-z])”匹配成功的位置是位置 0,所以“[a-z0-9]+”也是从位置 0 开始尝试匹配的,“[a-z0-9]+”首先尝试匹配“a”,匹配成功,继续尝试匹配,可以成功匹配接下来的“1”和“2”,此时已经匹配到位置 3,位置 3 的右侧已没有字符,这时会把控制权交给“$”;

元字符“$”从位置 3 开始尝试匹配,它匹配的是结束位置,也就是“位置 3”,匹配成功。

此时正则表达式匹配完成,报告匹配成功。匹配结果为“a12”,开始位置为 0,结束位置为 3。其中“^”匹配位置 0,“(?=[a-z])”匹配位置 0,“[a-z0-9]+”匹配字符串“a12”,“$”匹配位置 3。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿帕奇跨域_阿帕奇光束

阿帕奇跨域Apache Beam是一个开放源代码统一模型&#xff0c;用于定义批处理和流数据并行处理管道。 使用一种开源的Beam SDK&#xff0c;您可以构建一个定义管道的程序。 然后&#xff0c;该管道由Beam支持的分布式处理后端之一执行&#xff0c;这些后端包括Apache Apex &…

unity 启动相机_Unity3D研究院之打开照相机与本地相册进行裁剪显示(三十三)...

最近做项目需要用到这个功能&#xff0c;就是在Unity中调用Android本地相册或直接打开摄像机拍照并且裁剪一部分用于用户头像&#xff0c;今天研究了一下&#xff0c;那么研究出成果了MOMO一定要分享给大家。Unity与Android的交互还有谁不会&#xff1f;&#xff1f; 如果有不会…

深度学习pytorch--MNIST数据集

图像分类数据集&#xff08;Fashion-MNIST&#xff09; 在介绍softmax回归的实现前我们先引入一个多类图像分类数据集。它将在后面的章节中被多次使用&#xff0c;以方便我们观察比较算法之间在模型精度和计算效率上的区别。图像分类数据集中最常用的是手写数字识别数据集MNIS…

html 元素的属性

全局属性 全局属性是可与所有 HTML 元素一起使用的属性。 事件属性 用来定义某个事件的操作的属性叫事件属性&#xff0c;例如&#xff0c;οnclick“script”&#xff0c;元素上发生鼠标点击时触发 click 事件&#xff0c;click 事件被触发就会执行对应的脚本代码。事件属性…

nosql和rdnms_用于SaaS和NoSQL的Jdbi

nosql和rdnms一个自然的接口&#xff0c;用于与CRM&#xff0c;ERP&#xff0c;会计&#xff0c;营销自动化&#xff0c;NoSQL&#xff0c;平面文件等基于Java的数据集成 Jdbi是用于JavaSQL便利库&#xff0c;它为JDBC提供更自然的Java数据库接口&#xff0c;该接口易于绑定到…

matlab 功率谱密度 汉宁窗_如何理解随机振动的功率谱密度?

一、随机信号和正太分布有什么关系&#xff1f; 二、时域、频域之间功率守恒&#xff1f; 三、自相关又是个什么玩意&#xff1f;作为一个工程师&#xff0c;很多人对随机振动看着熟悉&#xff0c;却又实际陌生。熟悉是因为几乎每个产品在出厂时都要求要做随机振动试验&#xf…

深度学习pytorch--softmax回归(二)

softmax回归的从零开始实现实验前思考获取和读取数据获取数据集查看数据集查看下载后的.pt文件查看mnist_train和mnist_test读取数据集查看数据迭代器内容初始化模型参数定义softmax函数定义模型定义损失函数计算分类准确率模型评价--准确率开始训练可视化总结完整代码实验前思…

HTML块级元素/块标签/块元素

文章目录块元素的特点块元素清单block level element. 块级元素想在同一行显示需浮动或者 display:inline。 块元素的特点 每个块级元素都是独自占一行&#xff0c;其后的元素也只能另起一行&#xff0c;并不能两个元素共用一行&#xff1b; 元素的高度、宽度、行高、顶边距、…

物联卡查询流量_电信物联卡官网是多少?如何快速查询流量信息?

高速率设备的使用场景需要用到5G&#xff0c;中速率LET-Cat1应用范围更广&#xff0c;而低速率则要靠窄带物联网NB-IOT去维护了。这三种网络制式全都与物联网息息相关&#xff0c;这就能知道为什么国家层面对物联网基础设施建设这么重视了。电信物联卡在智能化硬件中有优秀表现…

java8日期转时间戳_Java 8日期和时间

java8日期转时间戳如今&#xff0c;一些应用程序仍在使用java.util.Date和java.util.Calendar API&#xff0c;包括使我们的生活更轻松地使用这些类型的库&#xff0c;例如JodaTime。 但是&#xff0c;Java 8引入了新的API来处理日期和时间&#xff0c;这使我们可以对日期和时间…

HTML行内元素/行级元素/内联元素/行标签/内联标签/行内标签/行元素

文章目录行内元素的特点行内元素清单可变元素列表inline element. 也叫行级元素、内联元素。行内元素默认设置宽度是不起作用&#xff0c;需设置 display:inline-block 或者 block 才行。 行内元素的特点 可以和其他元素处于一行&#xff0c;不用必须另起一行&#xff1b; 元…

深度学习pytorch--softmax回归(三)

softmax回归的简洁实现获取和读取数据定义和初始化模型softmax和交叉熵损失函数定义优化算法模型评价训练模型小结完整代码前两篇链接: 深度学习pytorch–softmax回归(一) 深度学习pytorch–softmax回归(二) 本文使用框架来实现模型。 获取和读取数据 我们仍然使用Fashion-M…

正则表达式的分类

文章目录一、正则表达式引擎二、正则表达式分类三、正则表达式比较四、Linux/OS X 下常用命令与正则表达式的关系一、正则表达式引擎 正则引擎大体上可分为不同的两类&#xff1a;DFA 和 NFA&#xff0c;而 NFA 又基本上可以分为传统型 NFA 和 POSIX NFA。 DFA(Deterministic …

spock测试_使用Spock测试您的代码

spock测试Spock是针对Java和Groovy应用程序的测试和规范框架。 Spock是&#xff1a; 极富表现力 简化测试的“给定/何时/然后” 语法 与大多数IDE和CI服务器兼容。 听起来不错&#xff1f; 通过快速访问Spock Web控制台&#xff0c;您可以非常快速地开始使用Spock。 当您有…

深度学习pytorch--多层感知机(一)

多层感知机隐藏层激活函数ReLU函数sigmoid函数tanh函数多层感知机小结我们已经介绍了包括线性回归和softmax回归在内的单层神经网络。然而深度学习主要关注多层模型。在本节中&#xff0c;我们将以多层感知机&#xff08;multilayer perceptron&#xff0c;MLP&#xff09;为例…

太阳能板如何串联_光伏板清洁专用的清洁毛刷

光伏发电是利用半导体界面的光生伏特效应将光能直接转变为电能的一种技术。主要由太阳电池板&#xff08;组件&#xff09;、控制器和逆变器三大部分组成。主要部件由电子元器件构成。太阳能电池经过串联后进行封装保护可形成大面积的太阳电池组件&#xff0c;再配合上功率控制…

java 异步等待_Java中的异步等待

java 异步等待编写异步代码很困难。 试图了解异步代码应该做什么的难度更大。 承诺是尝试描述延迟执行流程的一种常见方式&#xff1a;首先做一件事&#xff0c;然后再做另一件事&#xff0c;以防万一出错时再做其他事情。 在许多语言中&#xff0c;承诺已成为协调异步行为的实…

cass生成曲线要素_干货在线 | CASS入门指南——道路断面计算土方

CASS操作指南——道路断面计算土方法小伙伴们赶紧学起来&#xff01;道路类的土方工程&#xff0c;主要用CASS的断面法土方计算之道路断面来计算。整个计算过程主要分为以下四步&#xff1a;菜单截图第一步&#xff1a;绘制道路中心线道路的中心线&#xff0c;一般由直线段和缓…

正则表达式的捕获性分组/反向引用

文章目录分组捕获性分组和反向引用分组 正则的分组主要通过小括号来实现&#xff0c;括号包裹的子表达式作为一个分组&#xff0c;括号后可以紧跟限定词表示重复次数。如下&#xff0c;小括号内包裹的 abc 便是一个分组: // (abc) 表示匹配一个或多个"abc"&#xf…

深度学习pytorch--多层感知机(二)

多层感知机的从零开始实现获取和读取数据定义模型参数定义激活函数定义模型定义损失函数训练模型小结我们已经从上一节里了解了多层感知机的原理。下面&#xff0c;我们一起来动手实现一个多层感知机。首先导入实现所需的包或模块。 import torch import numpy as np获取和读取…