一起学习正则表达式(六)正则匹配原理

思维导图.png

转载请注明出处:https://www.jianshu.com/p/5b3316b95fe6

本文出自 容华谢后的博客

往期回顾:

《一起学习正则表达式(一)那些让人头晕的元字符》

《一起学习正则表达式(二)量词与贪婪》

《一起学习正则表达式(三)分组与引用》

《一起学习正则表达式(四)常见的4种匹配模式》

《一起学习正则表达式(五)断言匹配》

《一起学习正则表达式(六)正则匹配原理》

0.写在前面

在前面的文章中,我们学习了正则表达式的基础元字符,贪婪匹配还有一些常用的匹配模式,学会了这些,工作中的大部分问题,我们都可以做到游刃有余的解决。

But,仅仅是这些还不够,今天我们一起来学习下正则的匹配原理,知其所以还要知其所以然,学完本篇文章,相信各位可以写出更加优美的正则表达式。

1.有穷状态自动机

如果看过《编译原理》这本书,对有穷状态自动机这个词一定不陌生,有穷是指一个系统具有有穷个状态,不同的状态代表不同的意义。自动机是指系统可以根据相应的条件,在不同的状态下进行转移,从一个初始状态到达终止状态。

以正则表达式 ab|ac 为例:

ab|ac

1 是初始状态,输入 a 后就转移到状态 2 上,接着再输入 b 可以转移到状态 3 上,或者输入 c 可以转移到状态 4 上,图中蓝色的圆圈代表可接受的结束状态,结束状态可以是一个,也可以是多个。

有穷状态自动机就是一个识别器,它对每个输入的字符做识别和判断,来确定下一步该走向哪里,有穷状态状态机分为两种,分别是:

  • 确定性有穷自动机(Deterministic Finite Automata,DFA)

  • 非确定性有穷自动机(Nondeterministic Finite Automata,NFA)

2.DFA原理

DFA 是确定性有穷自动机的简称,因为是确定性的,所以 DFA 的特点是,每一个状态都能够基于下一个输入的字符,进行确定的转换,以 ab*c 为例:

ab*c

可以看到上图中,在状态 1 下,输入 a 可以到达状态 2,输入 c 可以到达状态 3,在状态 2 下,输入 b 还是在状态 2,输入 c 可以到达状态 3,其中的每一步状态转移都是确定的。

3.NFA原理

NFA 是非确定性有穷自动机的简称,和 DFA 相反,在某些状态的输入下,不能做确定性的状态转换,分为两种情况:

  • 同一个输入字符,可以转换到不同的状态

  • 接受空字符 ε,也就是不读入字符就跳转到另一个状态上

注:ε:epsilon,音标/ep'silon/,中文读音为“艾普西隆”

现在有这样一个需求,匹配以字母 a 开头,字母 b 结尾,中间是任意多(可以是 0 个)英文小写字母的字符串,我们可以很快的写出正则表达式 a[a-z]*b,看下状态图:

a[a-z]*b

当要匹配的目标字符串是 aabb 时,在状态 1 下,输入 a 到达状态 2,在状态 2 下,输入第二个 a 还在状态 2,这个时候输入 b,既可以保持在状态 2 下,还可以转移到状态 3,这个时候状态是不确定的,只有输入第二个 b 之后,才能知道上一步应该向什么状态转移。

上面的状态图,还可以用 ε-NFA 来表示:

a[a-z]*b(ε-NFA)

ε 代表空字符,如果要匹配的目标字符串是 ab,路径就是:状态1 —> a —> 状态2 —> ε —> 状态5 —> b —> 状态6

4.DFA工作机制

DFA 引擎的工作方式是,先看文本再看正则,以文本为主导,举个栗子:

目标字符串:Toady is such a beautiful day

正则表达式:beau(x|tify|tiful)

想象一下,你手里拿着一张彩票(文本),在看电视中的开奖号码(正则),看一眼第一个 T 不对,接着 o、a、d、y... 依次看下来,到了 b 终于对了一个,接着又对上了 e、a、u 三个字母,有戏:

text: Toady is such a beautiful day
                         ^
regex: beau(x|tify|tiful)
          ^

继续往后看,彩票中 u 的后面是 t,看一眼开奖号码的三个奖项,一等奖 x 不符合,二等奖和三等奖都是以 t 开头的:

text: Toady is such a beautiful day
                          ^
regex: beau(x|tify|tiful)
            ^ ^    ^
            ✘✔    ✔

接着看彩票的 i、f、u、l 部分,发现中了三等奖,Congratulations!

text: Toady is such a beautiful day
                          ^
regex: beau(x|tify|tiful)
            ^ ^     ^
            ✘ ✘    ✔

可以发现,DFA 以文本为主导,目标字符串只看一遍,不会发生回溯,相同的字符不会被测试多次。

5.NFA工作机制

NFA 引擎的工作方式和 DFA 相反,是先看正则再看文本,以正则为主导,还是用上面的栗子。

这次是先看开奖号码(正则),再看你手里的彩票(文本),开奖号码第一个字母是 b,在彩票中找到了 b,接着看开奖号码后面的 e,看下彩票中 b 后面是 e,就这样找到了 beau:

regex: beau(x|tify|tiful)
          ^
text: Toady is such a beautiful day
                         ^

接着再看彩票 beau 的后面是不是 x,发现不是,一等奖擦肩而过:

regex: beau(x|tify|tiful)
            ^
            ✘
text: Toady is such a beautiful day
                          ^

继续看二等奖,开奖号码 t、i、f 都符合,到了 y 发现彩票里的是 u,二等奖擦肩而过:

regex: beau(x|tify|tiful)
                 ^
                 ✘
text: Toady is such a beautiful day
                             ^

再看三等奖,重新从 t 开始比较(回溯,NFA 引擎会记住这里),发现都符合,又中奖了。

可以发现,NFA 以正则为主导,会发生回溯,目标字符串的同一部分,有可能会被反复测试很多次,因此,在最坏的情况下,它的执行速度可能会非常慢。

6.POSIX NFA

因为传统的 NFA 引擎急于报告匹配结果,找到第一个匹配上的就返回了,所以可能会导致还有更长的匹配未被发现,比如我们用正则 北京|北京市 来匹配文本 北京市,看下效果:

NFA

可以看到匹配到的结果是 北京,而 北京市 也是我们想要的匹配结果,并没有匹配上,这个时候 POSIX NFA 应运而生,它会在找到可能的最长匹配之前进行回溯,也就是说它会匹配最长的目标字符串,如果两边长度相同,则以左边的为准,所以POSIX NFA 引擎的速度要慢于传统的 NFA 引擎。

看下DFA、传统 NFA 以及 POSIX NFA 引擎的特点:

引擎类型 语言 忽略优先量词(懒惰) 捕获型括号 回溯
DFA MySQL、awk(大多数版本)、egrep(大多数版本)、
flex、lex、Procmail
不支持 不支持 不支持
传统型 NFA Golang、PCRE library、Perl、PHP、Java、Python、
Ruby、grep(大多数版本)、GNU Emacs、less、
more、.Net、sed(大多数版本)、vi
支持 支持 支持
POSIX NFA mawk、Mortice Kern Systems`utilities、
GNU Emacs(明确指定时使用)
不支持 不支持 支持
DFA/NFA 混合 GNU awk、GNU grep/egrep、Tcl 支持 支持 DFA 支持

7.写在最后

最后在总结下上面讲到的内容:

思维导图

到这里,正则表达式的匹配原理就讲完了,如果有问题可以给我留言评论,谢谢。

正则表达式在线校验工具:https://regex101.com/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,825评论 4 377
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,887评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,425评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,801评论 0 224
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,252评论 3 299
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,089评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,216评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 31,005评论 0 215
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,747评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,883评论 2 255
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,354评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,694评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,406评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,222评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,242评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,017评论 2 281

推荐阅读更多精彩内容