您现在的位置是:亿华云 > 人工智能
浅谈正则表达式原理
亿华云2025-10-04 00:51:45【人工智能】3人已围观
简介正则表达式可能大部分人都用过,但是大家在使用的时候,有没有想过正则表达式背后的原理,又或者当我告诉你正则表达式可能存在性能问题导致线上挂掉,你会不会觉得特别吃惊?我们先来看看7月初,因为一个正则表达式
正则表达式可能大部分人都用过,浅谈但是正则大家在使用的时候,有没有想过正则表达式背后的表达原理,又或者当我告诉你正则表达式可能存在性能问题导致线上挂掉,式原你会不会觉得特别吃惊?浅谈
我们先来看看7月初,因为一个正则表达式,正则导致线上事故的表达例子。
https://blog.cloudflare.com/d...
简单来说就是式原一个有性能问题的正则表达式,引起了灾难性回溯,浅谈导致cpu满载。正则
性能问题的表达正则
先看看出问题的正则
引起性能问题的关键部分是 .*(?:.*=.*) ,这里我们先不管那个非捕获组,式原将性能问题的浅谈正则看做 .*.*=.* 。
其中 . 表示匹配除了换行以外的正则任意字符(很多人把这里搞错,容易出bug),表达 .* 表示贪婪匹配任意字符任意次。
什么是回溯
在使用贪婪匹配或者惰性匹配或者或匹配进入到匹配路径选择的时候,遇到失败的匹配路径,尝试走另外一个匹配路径的网站模板这种行为,称作回溯。
可以理解为走迷宫,一条路走到底,发现无路可走就回到上一个三岔口选择另外的路。
回溯现象
// 性能问题正则 // 将下面代码粘贴到浏览器控制台运行试试 const regexp = `[A-Z]+\\d+(.*):(.*)+[A-Z]+\\d+`; const str = `A1:B$1,C$1:D$1,E$1:F$1,G$1:H$1` const reg = new RegExp(regexp); start = Date.now(); const res = reg.test(str); end = Date.now(); console.log(常规正则执行耗时: + (end - start))现在来看看回溯究竟是怎么一回事
假设我们有一段正则 (.*)+\d ,这个时候输入字符串为 abcd ,注意这个时候仅仅输入了一个长度为4的字符串,我们来分析一下匹配回溯的过程:
上面展示了一个回溯的匹配过程,大概描述一下前三轮匹配。
注意 (.*)+ 这里可以先暂且看成多次执行 .* 。 (.*){ 1,}
***次匹配,因为 .* 可以匹配任意个字符任意次,那么这里可以选择匹配空、a、ab、abc、abcd,因为 * 的贪婪特性,所以 .* 直接匹配了 abcd 4个字符, + 因为后面没有其他字符了,所以只看着 .* 吃掉 abcd 后就不匹配了,这里记录 + 的值为1,然后 \d 没有东西能够匹配,所以匹配失败,进行***次回溯。服务器托管
第二次匹配,因为进行了回溯,所以回到上一个匹配路径选择的时候,上次 .* 匹配的是 abcd ,并且路不通,那么这次只能尝试匹配 abc ,这个时候末尾还有一个 d ,那么可以理解为 .* ***次匹配了 abc ,然后因为 (.*)+ 的原因, .* 可以进行第二次匹配,这里 .* 可以匹配 d ,这里记录 + 的值为2,然后 \d 没有东西能够匹配,所以匹配失败,进行第二次回溯。
第三次匹配,因为进行了回溯,所以回到上一个匹配路径选择的时候,上次***个 .* 匹配的是 abc ,第二个 .* 匹配的是 d ,并且路不通,所以这里第二次的 .* 不进行匹配,服务器租用这个时候末尾还有一个 d , \d 和 d 匹配失败,进行第三次回溯。
如何减少或避免回溯
优化正则表达式:时刻注意回溯造成的性能影响。 使用DFA正则引擎的正则表达式什么是DFA正则引擎
传统正则引擎分为NFA(非确定性有限状态自动机),和DFA(确定性有限状态自动机)。
DFA
对于给定的任意一个状态和输入字符,DFA只会转移到一个确定的状态。并且DFA不允许出现没有输入字符的状态转移。
比如状态0,在输入字符A的时候,终点只有1个,只能到状态1。
NFA
对于任意一个状态和输入字符,NFA所能转移的状态是一个非空集合。
比如状态0,在输入字符A的时候,终点可以是多个,即能到状态1,也能到状态0。
DFA和NFA的正则引擎的区别
那么讲了这么多之后,DFA和NFA正则引擎究竟有什么区别呢?或者说DFA和NFA是如何实现正则引擎的呢?
DFA
正则里面的DFA引擎实际上就是把正则表达式转换成一个图的邻接表,然后通过跳表的形式判断一个字符串是否匹配该正则。
// 大概模拟一下 function machine(input) { if (typeof input !== string) { console.log(输入有误); return; } // 比如正则:/abc/ 转换成DFA之后 // 这里我们定义了4种状态,分别是0,1,2,3,初始状态为0 const reg = { 0: { a: 1, }, 1: { b: 3, }, 2: { isEnd: true, }, 3: { c: 2, }, }; let status = 0; for (let i = 0; i < input.length; i++) { const inputinputChar = input[i]; status = reg[status][inputChar]; if (typeof status === undefined) { console.log(匹配失败); return false; } } const end = reg[status]; if (end && end.isEnd === true) { console.log(匹配成功); return true; } else { console.log(匹配失败); return false; } } const input = abc; machine(input);优点:不管正则表达式写的再烂,匹配速度都很快
缺点:高级功能比如捕获组和断言都不支持
NFA
正则里面NFA引擎实际上就是在语法解析的时候,构造出的一个有向图。然后通过深搜的方式,去一条路径一条路径的递归尝试。
优点:功能强大,可以拿到匹配的上下文信息,支持各种断言捕获组环视之类的功能
缺点:对开发正则功底要求较高,需要注意回溯造成的性能问题
总结
现在回到问题的开头,我们再来看看为什么他的正则会有性能问题
首先他的正则使用的NFA的正则引擎(大部分语言的正则引擎都是NFA的,js也是,所以要注意性能问题产生的影响) 他写出了有性能问题的正则表达式,容易造成灾难性回溯。如果要避免此类的问题,要么提高开发对正则的性能问题的意识,要么改用DFA的正则引擎(速度快,功能弱,没有补货组断言等功能)。
注意事项
在平常写正则的时候,少写模糊匹配,越精确越好,模糊匹配、贪婪匹配、惰性匹配都会带来回溯问题,选一个影响尽可能小的方式就好。写正则的时候有一个性能问题的概念在脑子里就行。
很赞哦!(944)
相关文章
- 3、不明先知,根据相关征兆预测可能发生的事件,以便提前做好准备,赶紧注册相关域名。;不差钱域名;buchaqian抢先注册,就是这种敏感类型。预言是最敏感的状态。其次,你应该有眼力。所谓眼力,就是善于从社会上时不时出现的各种热点事件中获取与事件相关的域名资源。眼力的前提是对域名领域的熟悉和丰富的知识。
- 如何用Verdaccio搭建一个企业级私有Npm库
- R语言利剑之NoSQL系列:MongoDB
- 大数据时代下的车联网发展
- 并非一个好米任何人都会给你一个好的价格。那你该如何用以有的好米卖出最理想的价格呢?
- 一个罕见的MySQL redo死锁问题排查及解决过程
- 面试突击:如何正确停止线程?
- Spring系列:聊聊 @Scope 注解用法,你会了吗?
- 评估域名涉及的行业规模与发展状况成正比。
- JavaScript 新增两个原始数据类型
热门文章
站长推荐
2、定期提交和投标域名注册。例如,益华网络点击“立即预订”后,平台会抢先为客户注册域名。当然,一个域名可能会被多个客户预订,所以出价最高的人中标。
SQL Server第三方工具提供细粒度数据库恢复
MySQL innodb引擎备份工具XtraBackup之二(数据库全备)
MariaDB10和MySQL5.6社区版压力测试
a、变更前的公司证件扫描件(代码证或者营业执照)及联系人身份证复印件、变更后的公司证件扫描件(代码证或者营业执照)及新的联系人身份证复印件;身份证复印件需本人签名,公司证件复印件需加盖公章。
领导看了我写的关闭超时订单,让我出门左转!
彻底搞懂 npm、yarn 与 pnpm 依赖管理逻辑
MySQL在大数据、高并发场景下的SQL语句优化和最佳实践