转载请注明作者: phylips@bmy 出处: http://duanple.blog.163.com/blog/static/7097176720098303134160/

概述

在正则表达式领域,有一本广为推崇的书籍<<精通正则表达式>>,但是作者在书中的很多地方假设那些匹配引擎采用的是回溯的算法。但是实际情况是有些引擎采用的NFA,DFA模拟算法,比如grep,awk,sed,对于它们来说算法复杂度是多项式级的。同时采用回溯的一些引擎也在逐步改进,比如通过采用备忘录方法,记住已经回溯到达的状态,防止重复,也可以避免指数级的复杂度。

所以实际上这本书中提到的很多优化方法,未来可能都是不必要的了。同时这本书对正则匹配引擎原理的介绍,也不过深入,只是一个概述性的,所以有些让我失望,因为当初买这本书的目的就是希望可以藉此深入正则匹配的内部原理。有鉴于此,所以才有了这一篇文章的诞生。这篇文章主要参考了Brian W. Kernighan and Rob Pike对正则表达式的一个递归实现,以及Russ Cox 所写的Regular Expression Matching Can Be Simple And Fast,这篇文章对grep的实现进行了更为详细的介绍。

实现一个正则匹配引擎,实际上就类似与实现一个简单语言的编译器。一个正则表达式就是用正则符号写出的程序,我们要对这个式子进行语法分析,建立一个语法分析树,根据这个树生成NFA,如果采用NFA匹配的话,然后需要写出NFA模拟执行的程序,用来进行匹配。而正则匹配引擎,本身与lex的实现很类似,所以基本上可以了解到词法分析和语法分析的简单内容。所以一方面我们可以了解正则匹配最深层的原理,另一方面也是对编译原理的应用实践。而且工作量始终,一个简单的grep实现也就大概500c语言代码。

当然设计时我们需要考虑这个正则匹配引擎的可扩展性,比如支持unicode,支持自定义的字符集合,可以方便添加一些新的运算,这样下了,我们可以方便的逐步实现一个linux的grep扩展。另外可以实现一个java版本的,以代替指数级复杂度的实现。

首先我们来看一个简化了的正则表达式,以及如何用递归回溯的方式,以最少的代码实现它。

然后再来看一个grep的NFA实现,实际上它的算法早在1968年Ken Thompson,“Regular expression search algorithm,”中发表了。然而到了今天,很多语言(perl python pcre库)的正则匹配引擎却采用了一些更为低级的算法,这样的一些算法主要利用了回溯的方法,但是极端情况下却会达到指数级的复杂度。采用这种回溯的方法可能是因为实现者已经忘记了Ken Thompson的算法。另一方面,很多语言的正则表达式引入了前向引用(backreference)。这样就使正则匹配超出了正则的范畴,这个情况下的匹配实际上是NP完全问题,一般也只能通过回溯解决,所以掌握递归回溯的方法也是一种需求。但是即使在这样的匹配中,我们仍然可以把NFA作为一种方案,在不出现backreference时使用NFA匹配。

同时我们可以看到,在grep采用的是最短匹配算法,这是与该应用本身相关的,因为它的目的是搜索,但是对于一些以替换为目的的正则匹配,则希望找到那个最长的匹配,这也是很多其他语言比如perl采用的默认选择。

递归实现

一个简化定义下的纯递归实现,可以参考<<代码之美>>第一章,只选择了^.c*$这5个运算符

#include <stdio.h>
#include <iostream>
int match_star(int c,char *regexp,char*text);
int match_here(char *regexp,char*text){
if(regexp[0] == '\0')return 1;
if(regexp[1] == '*') return match_star(regexp[0],regexp+2,text);
if(regexp[0] == '$') return text[0] == '\0';
if(regexp[0] == '.' && text[0] != '\0') return match_here(regexp+1,text+1);
if(regexp[0] == text[0] && text[0] != '\0') return match_here(regexp+1,text+1);
return 0;

}

int match_star(int c,char*text){ do{ if(match_here(regexp,text) == 1) return 1; } while((*text) != '\0' &&(*text++ == c || c == '.')); return 0; } int main() { char *a = "a*$"; char *b = "aab"; printf("%d",match_here(a,b)); int printf = printf(); cout << printf; getchar(); return 0; }

正则表达式原理及引擎简化递归实现的更多相关文章

  1. HTML5数字输入仅接受整数的实现代码

    这篇文章主要介绍了HTML5数字输入仅接受整数的实现代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  2. ios – 使用大写符号在字符串swift中获取URL的正则表达式

    我尝试在文本中获取URL.所以,在此之前,我使用了这样一个表达式:但是当用户输入带有大写符号的URL时(例如Http://Google.com,它与它不匹配)我遇到了问题.我试过了:但什么都没发生.解决方法您可以使用正则表达式中的i内联标志关闭区分大小写,有关可用正则表达式功能的详细信息,请参阅FoundationFrameworkReference.(?ismwx-ismwx)Flagsetti

  3. ios – 如何在Swift 3中使用正则表达式?

    解决方法我相信.当没有其他选项适用时,将使用.allZeros.因此,使用Swift3,您可以传递一个空的选项列表或省略options参数,因为它默认为无选项:要么请注意,在Swift3中,您不再使用error参数.它现在抛出.

  4. ios – lldb断点在类目标c中的所有方法

    如何使用lldb在ObjectiveC类中的所有方法上自动设置断点?

  5. ios – 嵌套递归函数

    我试图做一个嵌套递归函数,但是当我编译时,编译器崩溃.这是我的代码:编译器记录arehere解决方法有趣的…它似乎也许在尝试在定义之前捕获到内部的引用时,它是bailing?以下修复它为我们:当然没有嵌套,我们根本没有任何问题,例如以下工作完全如预期:我会说:报告!

  6. swift override --有一个递归问题未解决

    classca{varcount:Int{get{return1;}set{self.count=newValue;}}funcdescribe()->String{return"ca";}}classcb:ca{overridefuncdescribe()->String{return"cb";}overridevarcount:Int{get{return2;}set{//引起了递归调用,未找

  7. swift的正则表达式(NSRegularExpression)

    init(_pattern:String){varerror:NSError?

  8. Swift2.0语言教程之函数嵌套调用形式

    Swift2.0语言教程之函数嵌套调用形式Swift2.0语言函数嵌套调用形式在Swift中,在函数中还可以调用函数,从而形成嵌套调用。以下将对这两种调用进行详细讲解。调用方式如图7.4所示。图7.4函数嵌套的形式以下将使用函数的嵌套调用实现对s=22!这个数值,即调用f1()函数,计算22,结果为4,然后在调用f2()函数,对4的阶乘求取,计算完成22!但是在Swift语言中递归必须要有一个满足结束的条件。

  9. 【Swift】学习笔记(九)——枚举

    因为类完全可以替代枚举。不过swift中也有许多类的特性被枚举支持。这样判断必须穷举所有成员,否则就需要增加default这个选项了。使用递归枚举时,编译器会插入一个中间层。

  10. swift 正则表达式运用实例选自《swifter 100个swift开发必备tip 》

随机推荐

  1. 法国电话号码的正则表达式

    我正在尝试实施一个正则表达式,允许我检查一个号码是否是一个有效的法国电话号码.一定是这样的:要么:这是我实施的但是错了……

  2. 正则表达式 – perl分裂奇怪的行为

    PSperl是5.18.0问题是量词*允许零空间,你必须使用,这意味着1或更多.请注意,F和O之间的空间正好为零.

  3. 正则表达式 – 正则表达式大于和小于

    我想匹配以下任何一个字符:或=或=.这个似乎不起作用:[/]试试这个:它匹配可选地后跟=,或者只是=自身.

  4. 如何使用正则表达式用空格替换字符之间的短划线

    我想用正则表达式替换出现在带空格的字母之间的短划线.例如,用abcd替换ab-cd以下匹配字符–字符序列,但也替换字符[即ab-cd导致d,而不是abcd,因为我希望]我如何适应以上只能取代–部分?

  5. 正则表达式 – /bb | [^ b] {2} /它是如何工作的?

    有人可以解释一下吗?我在t-shirt上看到了这个:它似乎在说:“成为或不成为”怎么样?我好像没找到’e’?

  6. 正则表达式 – 在Scala中验证电子邮件一行

    在我的代码中添加简单的电子邮件验证,我创建了以下函数:这将传递像bob@testmymail.com这样的电子邮件和bobtestmymail.com之类的失败邮件,但是带有空格字符的邮件会漏掉,就像bob@testmymail也会返回true.我可能在这里很傻……当我测试你的正则表达式并且它正在捕捉简单的电子邮件时,我检查了你的代码并看到你正在使用findFirstIn.我相信这是你的问题.findFirstIn将跳转所有空格,直到它匹配字符串中任何位置的某个序列.我相信在你的情况下,最好使用unapp

  7. 正则表达式对小字符串的暴力

    在测试小字符串时,使用正则表达式会带来性能上的好处,还是会强制它们更快?不会通过检查给定字符串的字符是否在指定范围内比使用正则表达式更快来强制它们吗?

  8. 正则表达式 – 为什么`stoutest`不是有效的正则表达式?

    isthedelimiter,thenthematch-only-onceruleof?PATTERN?

  9. 正则表达式 – 替换..与.在R

    我怎样才能替换..我尝试过类似的东西:但它并不像我希望的那样有效.尝试添加fixed=T.

  10. 正则表达式 – 如何在字符串中的特定位置添加字符?

    我正在使用记事本,并希望使用正则表达式替换在字符串中的特定位置插入一个字符.例如,在每行的第6位插入一个逗号是什么意思?如果要在第六个字符后添加字符,请使用搜索和更换从技术上讲,这将用MatchGroup1替换每行的前6个字符,后跟逗号.

返回
顶部