正则小结

  正则就是一段描述匹配规则的字符串,经解析,能匹配想要的字符串。就比如受害者描述嫌疑人的体貌特征,警察会从众多体貌相近的人中进行筛选,描述得准确,能够减小排查范围,加快破案的速度。传统型NFA就是典型的例子,决定权在于你, 看你的如何描述,如引导正则引擎去高效地匹配相应内容。大多数人看到正则晦涩难懂,只要求会用, 不去考究它的工做原理, 是很难活学活用的。本身也差很少, 很难系统地去学习正则, 非要写的时候也是参考手册写的, 此次可贵有闲情雅致去啃这硬骨头, 比之前了解得稍微多了点, 在此作个小结。
框架

  我的是在JS中使用正则的,其中的正则是Perl正则的一个完整的子集,属于传统型NFA。正则引擎分为DFA,传统型NFA ,POSIX NFA。DFA和NFA的最大区别是, DFA是文本主导的,每一个字符只会检查一遍,不会回溯,不支持捕获型括号,遵循最左最长原则。POSIX NFA的典型特征是,虽然它是表达式主导的,在匹配到第一个结果之后,会继续尝试全部可能性(分支),直到找到最长的匹配为止。因此相比较而言,POSIX NFA的速度最慢。(/a|ab/去匹配字符串'abc')。
学习

 


 

  举个较常见的例子,12312312345 ==> 12,312,312,345  (每三位之间加逗号)。分析一下,在添加逗号的地方,右侧是3的整数倍的数字,左侧是1-3个数字。若是对环视(预查)不陌生的话,能很快想到/(?<=\d)(?=(?:\d{3})+)/,将这些位置替换为逗号。可是?<=在js中是不被支持的,得脱离反向环视。s.replace(/\d(?=(?:\d{3})+)/g,function(){return arguments[0]+',';})能够很出色的完成这个任务,固然还有不少的替代方案,例如s.match(/\d(?=(?:\d{3})*)/).join(',') 这里和以前正则惟一的不一样就是+改为了*号,咱们须要最后3个数字,才能拼接成咱们想要的字符串。若是不使用环视呢,我能想到的办法,就是循环。优化

    var r = /(\d)((\d{3})+)$/;
        while(r.test(s)){
                s = s.replace(r,function(){
                    var arg = arguments;
                    return arg[1]+','+arg[2];
                });
        }
this

固然这样的代价是很大的,效率会低不少。可见达到目的,方法有不少,若是选择一个最合适的方法,必须得花点本钱花点时间去了解传统型NFA的工做原理。spa

 


 

  表达式匹配的过程对象

  1 编译为内部形式。 每一个表达式的行程都会消耗必定的资源,对于重复出现的表达式,必定要一次声明,多处使用,尤为是在循环中。
资源

  2 开始传动,正则引擎定位到目标字符串的起始位置。若是指定的lastIndex,就是从这个位置开始。
字符串

  3 依次检测表达式的各个元素,控制权在不一样的元素之间切换。
io

  4 寻找合适的结果。传统型一旦检测到合适的结果就会终止检索,若是有g修饰符,会从以前匹配的字符以后去检索,而POSIX NFA会尝试各类可能性,匹配最长的结果。
编译

  5 若是失败,会从步骤3从新开始。

  6 完全宣告失败。

 


  优化措施

  对其工做原理有必定了解之后,须要经过大量的实践才能概括出些许优化手段。固然我作的仍是不多的,参考书上,照搬到这里。

        1 行锚点优化 ^ $,  /dasd$/可以从字符串倒数第四位开始匹配。
              独立出^$, ^(and) 比 (^abc)效率高,由于第二个在检测锚点以前必须进入到字符串
          2 若是正则以 .* 开头,并且没有g, 可认为词表达式开头有一个看不见的^, 减小大量的回溯
          3 字符串链接优化,将abc当作一个元素,而不是三个
          4 消除没必要要的括号 (?:.)* -> .*
          5 消除没必要要的字符组 [.]->\.
          6 (非贪婪)->使用忽略优先量词的时候,引擎一般须要在量词做用对象和该对象以后的字符之间切换,速度较慢。
          "(.*?)"         *尝试匹配0,查看下一次字符是不是",依次迭代。另一个缺点就是,"在括号外面,会带来额外的开销。
          可使用[^"]来代替通配符(.), ?也能够去掉。
          7 避免指数级(super-linear 超线性)匹配
          8 ?>固化分组 和 ?++ 占有优先量词 避免回溯
          9 正则一次编译,屡次调用,(Perl)减小变量插值
          10 使用非捕获型的括号,避免滥用括号, 字符组
          11 提取多选结构开头的必须元素(this|that)->th(is|at)
          12 忽略优先仍是匹配优先,具体状况具体分析。
          好比^.*:  相比较于 ^.*?:
              前面的正则会匹配到最后一个:  然后面的匹配到第一个:
          若是只有一个冒号,如何取舍?
          若是:比较靠前 xxx:xxxxxxxxxxxxxxxxxxxxxxxxxx,使用忽略优先
          反之,使用匹配优先,由于忽略优先须要检测在量词和:之间切换,表现不如匹配优先; 而此时,匹配优先只须要少许的回溯就能够匹配。
          若是数据随机,优先使用匹配优先.
          13 多选结构中,出现概率的排在最前面
          14 取消循环


 

  传统型NFA是控制能力最强的正则引擎,考量正则的连个标准是准确性和效率,须要花内心在这两则之间找到平衡。因此以前的优化措施显得尤其重要。只有大量的实践以及精益求精的精神才能够提炼出接近完美的正则,虽然有时候正则引擎自己的优化以及强化会极大影响匹配效率,本身能作的是在框架之下作到最好,而且在合适的时机跳出这个框架的约束。

相关文章
相关标签/搜索