• 大家来找错自己写个正则引擎(一)概要介绍


      平时写代码肯定免不了犯错,如何减少犯错的机会呢,只能多加练习,多加注意。软件开发的难点是处理本质的复杂度,也就是业务逻辑本身的复杂性,而不是解决一些技术上的难题。作为开发人员,我们一方面要多看书,增加知识宽度,学习新技术,保持技术敏感,另一方面更要锻炼基本功,比如《代码大全》上写的那些,使用防御性编程,控制多层嵌套,有效使用递归,用伪代码设计子程序,练好code review本领等。

      本系列就是写一个小的正则引擎,技术上来说没什么高深的东西,用什么语言都可以实现,就是用一些if else,while,递归等普通的语句,只是分析起来有些难度,业务逻辑也稍微复杂一些,在写实现代码的时候要多加小心,很难一次写对,要不断的调试,改动,再调试,再改动。在你调试的时候你会发现问题的原因往往很简单,只不是你不够小心,犯了低级错误,比如循环中忘了递增循环控制变量,或者数组索引溢出,或者抛出空引用异常等。如果你够仔细,不犯这种低级错误,尽量一次写好,那会大大提高程序质量,有经验的程序员和菜鸟的一大区别就是写好代码后仔细review,尽量保证调试的过程中不会有低级错误,而菜鸟一般是写完后就急着调试,发现错误,就修改一下,然后再调试,发现改错了,再改,再调试,最终程序结果跑对了也不知道为什么结果是对的,其实只是碰巧对了而已,优秀的程序员会在写完代码后用大脑去跑一边算法,才会去调试。

      写这个系列目的是和各位有经验的程序员请教,我的代码虽然碰巧结果是对的,还有哪些BUG,哪些需要注意的地方,哪些地方可以写的更精简,哪些设计的扩展性不强,甚至哪个变量的命名太差,哪个if语句的判断条件应该提取出来,哪个地方应该有注释等等,总之就是大家来挑刺儿,把不好的地方总结出来,防止以后再犯错,其它围观的朋友也可受益。

      说是写一个正则引擎,但限于技术水平有限,也来不及仔细看相关基础理论,我们只实现正则的一些简单逻辑。正则表达式的匹配模式分三种:连接,或及闭包。

    1. “连接”很好理解,“ab”就是字符”a”和字符”b”的一个连接,做匹配的时候要保证匹配出来的结果必须有“a”有“b”,且顺序不能错。
    2. “或”的意思就是两个模式之要满足其中之一就可以,如”a|b”,匹配的结果是“a”也行,是“b”也行,没有顺序。
    3. “闭包”的意思就是这个模式的任意顺序任意数量的组合,比如“(a|b)*”,后面的*就是指闭包,可匹配”a”,”b”,”ab”,”bbaababab”。

      我们就实现这3种最基本的匹配模式,像\d这种转移字符,?,+这些匹配数量控制,括号分组等均不实现,另外为了保持简单我们用小括号来进行模式分组,比如两个子模式连接,复杂的子模式需要用括号括住,如“a(b|c)”左边的模式匹配字符a,右边的模式匹配b或者c,由于右边的模式是个复杂模式,就用括号括起来。另外就是或关系里如果其中一个字模式包含了另一个子模式,我们取最长的匹配。

      我们说清楚了需求,就得先考虑测试用例的设计,我们只做根据正则式的分词,就是给定一个正则式和一个输入,把它分割成一个数组,匹配模式的部分算一子项,不匹配模式的字符单独算一子项。

    正则式 输入 匹配结果
    a aabaab a-a-b-a-a-b
    ab aabaab a-ab-a-ab
    ab|ac abaaac ab-a-a-ac
    a|aa abaaac a-b-aa-a-c
    (a)*: aabaab aa-b-aa-b
    :(a|b)* aabbaxaabbb aabba-x-aabbb
    a(b|c) abaaac ab-a-a-ac
    a(b|c)*: abbaaaccab abb-a-a-acc-ab

    因为功能简单,咱们设计也简单一些,不去管理论上的哪些DFA,NFA等,咱们就靠现有的知识实现个山寨的。

     image

      我们先把输入的模式字符串要抽象出一个模式树,模式树用PatterNode来表示,它有个Nodes属性来保存子节点,Text是自身所匹配的子模式表达,Releation是指子节点之间的关系是连接关系还是或的关系,OneOrMore是指是否匹配一个或多个,也就是闭包。PatternParser类的静态方法Parse用来把一个字符串分析成模式树。

      我们知道,在c#里,委托表示一个可执行的方法,而且可以当参数或者返回值,也就是说在c#里,函数是一等公民,我们可以在一定程度上使用函数式编程的优势,有了模式树,我们还要一个正则解析树RegexNode,该类有一个Parse属性,指向一个ParseFunc的委托,该委托用于解析本节点所代表的子模式,同时递归的执行本节点的子节点代表的子模式,该类的其它属性均和PatternNode相同,目的是为了调试时看着方便,以及由主控函数来使用。该类的SplitWords把一个字符串输入根据本身做代表的正则式进行分割。RegexParser类把一个模式树转换成正则解析树。

      ParseFuncFactory类是一个委托工厂类,它利用闭包原理生成一些委托供RegexNode的Parse属性用,从名字可以看出,有生成最长匹配的MaxMatch,有生成连接关系的MatcheAnd,生成或关系的MatcheOr,以及生成这三种基本匹配模式的闭包模式OnorMoreMaxMatch,MatchOneOrMoreWithAnd,MatchOneOrMoreWithOr。

  • 相关阅读:
    实现字符串的翻转
    svn的安装和配置
    insert php code test
    收集的一些题
    制作多选框,并通过PHP获取多选框数据
    laravel 导出
    laravel migrate 指定文件执行
    laravel facebook等第三方授权登录
    Mysql 时间字段(加上或者减去一段时间)
    配置指定日志记录目录
  • 原文地址:https://www.cnblogs.com/onlytiancai/p/1747552.html
Copyright © 2020-2023  润新知