Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >正则表达式-引擎

正则表达式-引擎

作者头像
Dylan Liu
发布于 2019-07-01 03:59:18
发布于 2019-07-01 03:59:18
9000
举报
文章被收录于专栏:dylanliudylanliu

对正则表达式的表象有了一定了解之后,我们其实对正则表达式的引擎原理是有一定猜测的,不过具体引擎是否是按我们猜想的那样运行,在我们深入了解之前肯定会发现自己有时构造的表达式与引擎执行的结果存在偏差,这就需要我们去挖掘表达式背后的英雄:引擎

引擎的分类

现在基本所有的文字编辑软件都会包含正则表达式的功能,但是不同的编辑器所使用的引擎实现原理是不一样的,现在大家用的有三种引擎:

  • DFA (deterministic finite automation)
  • NFA (Non-deterministic finite automation)
    • traditional NFA
    • POSIX NFA

预备知识-有限状态自动机

有限状态自动机(Finite state machine)表示有限个状态以及在状态之间转换和动作等行为的模型

有限状态自动机包含初始态,转换态和终态,当字符串匹配到达终态时匹配成功,未达到终态则是匹配失败,每个状态后续都是唯一确定的,我们使用状态转移图来表示:

有限状态机是不满足正则表达式引擎的要求的,因为正则表达式对应有分支,状态可能会存在多个等情况,所以延伸出了以下两种引擎

DFA

DFA是确定性有限自动机,它会先扫描表达式,将表达式编译成内部形式,然后在读入字符后状态可以到达多个,DFA记录所有可能的状态,线性的向后处理。但因为DFA只含有有限多个状态,所以不支持反向引用,不可以捕获表达式,但相对NFA来说,DFA的速度是稳定的,对于一些简单场景来说是足够了。

举例说明:对于正则表达式ac|ad|adc|ea,我们假设匹配adc,首先读入字符a, DFA记录后续可能的状态ac,ab,adc,ea不符合直接抛弃,读入字符d, 状态变换为ad,adc, 此时ad已经到达最终态,是一个可能的匹配,记录下来,然后读入c,只剩下adc,也到达最终态,DFA在默认情况下会匹配最长的串,所以匹配结果会是adc,作为对比,传统型NFA会匹配ad,POSIX NFA匹配结果是adc,后面来看他们的差异原因。

NFA

NFA不会像DFA记录所有的状态,它只会记录中间状态。其实与其将NFA看作有穷自动机,不如将NFA看作一棵树,NFA使用深度优先遍历的方式来向下匹配,匹配到叶子节点就完成一次匹配,匹配不成功就回溯到上一个节点,继续对未访问过的子节点进行匹配。

我们从关键概念入手来了解

传动

引擎会从字符串开始进行匹配,如果匹配不成功,则向后移动一个字符,再进行匹配,这个过程就是传动。

回溯

引擎需要匹配完所有路径才能报告匹配失败,在这个过程中,一条路径匹配不成功需要回溯到上一个节点继续其他为匹配的路径,这个过程叫回溯。

捕获、分组与反向引用

在正则里面,捕获跟分组是两个功能,不过捕获必须会有分组功能,分组可以不捕获。

正常的括号()包含捕获和分组的功能,也就是说可以使用\1 \2的方式来引用括号中匹配到的内容,但是捕获是需要记录状态的,在回溯时还需要更改状态,对效率有一定损失,如果对捕获的内容不再使用的话,可以使用非捕获分组(?:),这样就可以只使用分组功能。

反向引用与捕获是一对,前面捕获了后面才能使用反向引用,就是一个记录与使用的过程。

譬如我们要查找连续两个字母,我们可以使用(\w)\1

ps: 反向引用1/2/3的计数是按照左括号的计数位置来确定的,((\w)\w)(\d),这个表达式\1引用的是(\w)\w,\2引用的是\w,\3引用的是\d

占有字符和零宽度

看下面这张图:

一个有n个字符的字符串含有n+1个位置,^a正则中,^匹配了开始位置0,a是占有字符

前面我们使用的环视就是一个零宽度的位置匹配,它并不占有实际字符,只是作为一个条件。实际上零宽度是需要窥探后面或前面的字符的,只是他们在使用完之后又交还给引擎,并不占有这些字符。

贪婪匹配

正则引擎默认是使用贪婪的模式来匹配字符的,也就是说ab*b匹配abbbbb时,匹配结果为abbbbb,在遇到b时,会首先由b*来匹配,一直到结尾不能匹配再向后匹配,发现是b,而结尾与b不能匹配,于是回溯到最后一个b,匹配成功。

我们可以在量词后加问号?来提示使用非贪婪匹配,也就是说先匹配后面的,再来匹配自己,上面例子匹配结果就是ab。可以看出来贪婪匹配跟非贪婪匹配对于引擎来说就是一个先走哪个分支的问题,对我们来说匹配的结果差异是很大的。

占有优先与固化分组

在回溯中我们看到,如果后续的字符或模式不能匹配时,需要到回溯到上一个字符处继续匹配,这种情形我们说前面匹配的模式交还了一个字符,也就是说已经吃进去的字符再吐出来,这种情形下在遇到不匹配的模式时会一直重复吃-吐,会造成很大的浪费。

占有优先匹配是固化分组的一个简便表示方式,固化分组可以将已经匹配的字符固定在自己这里,回溯的时候不会往外吐字符,而是会直接匹配失败,这在我们对匹配模式有清晰了解,确定不需要回溯时候使用,可以提高匹配效率,在不符合模式的时候尽快报告失败。

例子:假设我们要匹配email,按照前面写的简单模式[-0-9a-zA-Z_]+@\w+(\.\w+)+,假设要匹配的字符串为This_is_a_unmatch_test,很显然这个不符合我们的email模式,[-0-9a-zA-Z_]+会一直匹配到结尾的t,然后发现结束符不能匹配,交给后续的模式匹配,@也不能匹配,于是[-0-9a-zA-Z_]+模式会把匹配到的t吐出来,@还不能匹配,继续吐(回溯),直到开头,然后引擎传动一个字符,再由h重复上述过程。

可以看到我们浪费了很多尝试的机会,因为在@不能匹配后,[-0-9a-zA-Z_]+里匹配到的字符是怎么也不会有@的,所以这里的回溯是没有价值的,是浪费的。我们可以使用占有优先量词或者固化分组来让[-0-9a-zA-Z_]+匹配的字符据为己有,回溯时不往外吐字符,而是直接报告失败。最后写成[-0-9a-zA-Z_]++@\w+(\.\w+)+,有+变成了++

NFA总结

NFA使用了复杂的技术来匹配我们写的表达式,这就需要我们对引擎的实现有一定了解,上面给出了NFA引擎中重要的概念,理解了他们我们对以后写出来的正则会更有信心

现在一般编程语言中带有的正则表达式包都是NFA的,包括java,PerlPython等的,因为NFA支持反向引用,捕获等强大的功能,是正则实现完备性不可或缺的。

POSIX NFA

POSIX NFA在NFA的基础上加了一个限制,就是要求返回的匹配结果需要是最长匹配结果,也就是说POSIX NFA引擎会尝试所有分支,返回最长匹配的结果。在大部分情况下,POSIX NFA比NFA的效率要低很多,毕竟要把所有的可能都计算出来,再比较然后返回最长的。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
正则表达式与优化
用 NFA 自动机实现的比较复杂的正则表达式,在匹配过程中经常会引起回溯问题。大量的回溯会长时间地占用 CPU,从而带来系统性能开销。
WindCoder
2020/01/21
8560
深入正则表达式(3):正则表达式工作引擎流程分析与原理释义
作为正则的使用者也一样,不懂正则引擎原理的情况下,同样可以写出满足需求的正则,但是不知道原理,却很难写出高效且没有隐患的正则。所以对于经常使用正则,或是有兴趣深入学习正则的人,还是有必要了解一下正则引擎的匹配原理的。
周陆军
2020/06/06
1.9K0
正则表达式引发的惨痛代价
在一次小型项目开发中,我遇到过这样一个问题。为了宣传新品,我们开发了一个小程序,按照之前评估的访问量,这次活动预计参与用户量 30W+,TPS(每秒事务处理量)最高 3000 左右。
田维常
2020/05/19
2K0
正则表达式引发的惨痛代价
正则表达式性能优化
正则表达式是计算科学的一个概念,很多语言都实现了他,正则表达式使用一些特定的元字符来检索,匹配以及替换符合规则的字符串。
小土豆Yuki
2020/11/03
2.2K0
一文掌握开发利器:正则表达式
作者:mathe,腾讯QQ音乐前端开发工程师 正则表达式具有伟大技术发明的一切特点,它简单、优美、功能强大、妙用无穷。对于很多实际工作来讲,正则表达式简直是灵丹妙药,能够成百倍地提高开发效率和程序质量。 1. 正则常见规则 1.1 字符匹配 字符说明\转义符\d[0-9]。表示是一位数字。\D[^0-9]。表示除数字外的任意字符。\w[0-9a-zA-Z_]。表示数字、大小写字母和下划线。\W[^0-9a-zA-Z_]。非单词字符。\s[\t\v\n\r\f]。表示空白符,包括空格、水平制表符、 垂直
腾讯技术工程官方号
2020/09/17
1.3K0
精通正则表达式 - 打造高效正则表达式
        总的来说,提高正则表达式效率的关键在于彻底理解回溯背后的过程,掌握技巧来避免可能的回溯。
用户1148526
2023/10/14
8880
精通正则表达式 - 打造高效正则表达式
正则表达式的用法及原理
由于工作中和正则表达式打交道比较多,所以花了几天的时间系统学习了正则,在此总结一下。
Ritchie
2022/06/29
1.4K1
正则表达式优化
DFA (Deterministic Finite Automaton 确定有穷自动机): 常见的只有MySQL,文本主导,不支持反向引用和捕获括号,但快
林万程
2019/08/05
1.2K0
正则表达式高级
正则表达式高级 ——《精通正则表达式》 +Java/Go/Python官方文档 +多年经验 +实验结果 知识整理
林万程
2019/08/05
1.1K0
从0到1打造正则表达式执行引擎(一) 正则表达式转NFA
今天是五一假期第一天,这里先给大家拜个晚 咳咳!!祝大家五一快乐,我这里给大家奉上一篇硬核教程。首先声明,这篇文章不是教你如何写正则表达式,而是教你写一个能执行正则表达式的 执行引擎。 网上教你写正则表达式的文章、教程很多,但教你写引擎的并不多。很多人认为我就是用用而已,没必要理解那么深,但知道原理是在修炼内功,正则表达式底层原理并不单单是用在这,而是出现在计算机领域的各个角落。理解原理可以让你以后写字符串匹配时正则表达式能够信手拈来,理解原理也是触类旁通的基础。废话不多说,直接开始正式内容。
全栈程序员站长
2022/09/09
8590
从0到1打造正则表达式执行引擎(一) 正则表达式转NFA
正则表达式 引擎分类
DFA是文本主导,DFA引擎在扫描字符串时,会记录“当前有效”的所有匹配可能,因此目标文本中的每个字符最多只会检查一遍。
luoheng
2022/08/28
3480
正则表达式的回溯[转]
近期我在为Lazada卖家中心做一个自助注册的项目,其中的shop name校验规则较为复杂,要求: 1. 英文字母大小写 2. 数字 3. 越南文 4. 一些特殊字符,如“&”,“-”,“_”等 看到这个要求的时候,自然而然地想到了正则表达式。于是就有了下面的表达式(写的比较龊):
流柯
2020/01/15
1K0
正则表达式的回溯[转]
正则表达式基础
基本语法 基本语法_菜鸟教程 用\表示特殊形式或允许使用特殊字符,而不调用其特殊含义 不以任何特殊方式在字符串字面值中以'r'前缀处理反斜杠 所以r'\n'包含'\'和'n'两个字符,而'\n'
JNJYan
2019/01/18
7470
一篇值得收藏的正则表达式文章
目前越来越多的网站、编辑器、编程语言都已支持一种叫“正则表达式”的字符串查找“公式”,有过编程经验的同学都应该了解正则表达式(Regular Expression 简写regex)是什么东西,它是一种字符串匹配的模式(pattern),更像是一种逻辑公式。
龙哥
2020/02/26
8120
[翻译]正则引擎的几种分类
正则表达式引擎是正则表达式匹配算法的基础。其有多种不同的实现,但大多数都是基于Henry Spencer的NFA引擎。
xindoo
2024/08/07
860
刨根究底正则表达式之二——正则表达式基础
虽然本系列文章开篇会简单介绍正则表达式的一些基础知识,但主要限于本系列文章所想强调的要点,因此本系列文章并不适合用于入门。
笨笨阿林
2019/01/18
1.2K0
正则表达式-锚点及模式修饰符
本节已经把常用的元字符全部都罗列完了,Unicode相关的控制\p等没有列出,平常用不太多,把这些融汇贯通基本就可以解决90%的正则问题了。接下来我们来探讨一下正则引擎的原理,有助于我们写出正确、效率高的正则表达式。
Dylan Liu
2019/07/01
1.3K0
从0到1打造正则表达式执行引擎
今天是五一假期第一天,这里先给大家拜个晚 咳咳!!祝大家五一快乐,我这里给大家奉上一篇硬核教程。首先声明,这篇文章不是教你如何写正则表达式,而是教你写一个能执行正则表达式的执行引擎。 网上教你写正则表达式的文章、教程很多,但教你写引擎的并不多。很多人认为我就是用用而已,没必要理解那么深,但知道原理是在修炼内功,正则表达式底层原理并不单单是用在这,而是出现在计算机领域的各个角落。理解原理可以让你以后写字符串匹配时正则表达式能够信手拈来,理解原理也是触类旁通的基础。废话不多说,直接开始正式内容。
xindoo
2021/01/22
7970
从0到1打造正则表达式执行引擎
正则表达式和 CPU 100%有什么故事?
前几天线上一个项目监控信息突然报告异常,上到机器上后查看相关资源的使用情况,发现 CPU 利用率将近 100%。通过 Java 自带的线程 Dump 工具,我们导出了出问题的堆栈信息。 我们可以看到所
纯洁的微笑
2018/07/20
1.5K1
藏在正则表达式里的陷阱
前几天线上一个项目监控信息突然报告异常,上到机器上后查看相关资源的使用情况,发现 CPU 利用率将近 100%。通过 Java 自带的线程 Dump 工具,我们导出了出问题的堆栈信息。
陈树义
2018/06/19
2K8
相关推荐
正则表达式与优化
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档