有了rewrap,老板再也不担心我的正则了。
发布于 8 年前 作者 chaosim 3946 次浏览 来自 分享

rewrap,无痛正则

有了rewrap,老板再也不担心我的正则了。

传说今年世界卫生组织的的宣传语是:采用无痛正则,编码不留后患:)

言归正则。

正则表达式是程序员离不开的工具之一。然而,正则表达式使用上有很多令人头痛之处,主要有三:

  • 需要死记硬背
    不少带些神秘感的符号,总有个别的容易搞混,需要不时查查文档,耗费时间和心力。
  • 不能组合
    程序员喜欢和擅长的是组合和重用,但是这个必杀技对于正则表达式没有作用,造成的后果是不管多大的正则式都必需一个字符一个字符地堆砌在一行里,形成一段深奥难懂的密文和bug的渊薮。面对稍微复杂一点正则表达式的任务,很多程序员就感到心有余而力不足。因此正则式往往只被用在一些极为简单的匹配上。其实正则表达式完全可以做得更多。
  • 匹配结果处理比较麻烦
    使用正则表达式经常需要引用匹配的子模式,然而这也是让人头痛的一个方面,因为所有模式是按照某种规则来排序成一个数组,需要程序员细致地计算括号的次序和嵌套顺序。

因为这三个原因,才有了这句话:你有个问题,你选择用正则式来搞定它。现在,你有了两个问题。

rewrap是解决上述问题的一剂良药。用了它,可以对正则式命名并注册到一个registry中以资引用,从而解决了组合问题。可以对需要保存的匹配模式命名并存储到一个Object中,以方便使用部分匹配的子模式。此外,rewrap也提供了一些方法(比如not, optional, any, some, repeat, paren等),这些方法可以代替正则式的大多数元符号,帮助理解正则式,也有助于写出更大的正则式以解决更复杂的问题。

除了rewrap,一并还发布了两个配套的npm package。

  • regexp-frequent,收集了一些常用的正则式,可以单独使用,也可以作为rewrap的registry引用。

  • rewrap-patch,这个工具可以对String.prototype打一些配合rewrap的补丁(monkey patch),包括 match(), replace(), search() 这三个方法。虽然 monkey patch 不是好的编程实践,但是如果有适合的场景(绝大多数时机你并不需要它),rewrap-patch 会给你带来便利。

钱包身体双free,不留后患,无痛正则走起!

PS. 很遗憾正则表达式不支持从字符串的指定位置开始匹配,比如象这样:text.match(/someRegexp/, startIndex)。我研究了RegExp对象的lastIndex,好像它对此也没什么用。哪位在这方面有什么发现,望能赐教。有人会说可以text.slice(startIndex).match(/someRegexp/),这正好是我要排除的,因为反复slice是低效的,阻碍了针对同一字符串不同位置组合使用正则式。或者哪位在TC39说得上话,提议在这方面扩充一下,增加一个方法或者为match增加一个参数?我考虑不会有太大的兼容性问题。这个功能其实非常有用的。借用阿基米德的那句话:给我一个支点,我能支起整个地球;也可以说:给match增加一个参数,可以支起一门新的语言。因为这样就可以用正则式作为持续解析的解决方案,衍生出很多用法。

最后是链接:
rewrap: https://github.com/taijiweb/rewrap
regexp-frequent: https://github.com/taijiweb/regexp-frequent
rewrap-patch: https://github.com/taijiweb/rewrap-patch

性急的朋友,请复制下面的命令:
npm install rewrap
npm install regexp-frequent
也想用下面这个?
npm install rewrap-patch

3 回复

正则我是看了就忘,就是用的不多

    const rewrap = require('rewrap');

    const {re, wrap} = rewrap; // wrap is the same as rewrap;

    // This npm package contains some frequently used regexp.
    // It can be used as the registry for rewrap.
    const reBuiltins = require('regexp-frequent');
    rewrap.registry(reBuiltins);

    re(/^/)    // /^/

      .ref('digits')    // /^[0-9]+/

      .and(/abc/)    /^[0-9]+abc/

      .or(/bc/, /cd/)   // /^[0-9]+abc|bc|cd/

      // because the line below is leaded by re, 
      // so @digits will not refer to registry, 
      // and will generate regexp as is.
      .and(/@digits/)   // /^[0-9]+abc|bc|cd@digits/

      .wrap   // now switch to rewrap style

      // @digits will refer to registry, will be replaced by [0-9]+
      // necessary () is added automatically for or expression
      .and(/@digits/)   // /(?:^[0-9]+abc|bc|cd)@digits[0-9]+/

      // &a indicate that a will become (a) 
      // and will be saved as result.$$rewrapData.a while matching
      .and(/a&a/)   // /^[0-9]+abc|bc|cd@digits[0-9]+(a)/

      .and(

         // generate (xy)
         // will be saved as result.$$rewrapData.xy while matching
         // same as wrap(/(xy)&xy/)
         wrap('x').and('y').save('xy')    // /(xy)/

      ) //   /(?:^[0-9]+abc|bc|cd)@digits[0-9]+(a)(xy)/

      // the whole above will be saved as result.$$rewrapData. part1 while matching
      .save('part1')

      // add current rewrap to registry as sample
      .register('sample')

      // refer to sample
      .rewrap(/@sample/)    // /((?:^[0-9]+abc|bc|cd)@digits[0-9]+(a)(xy))((?:^[0-9]+abc|bc|cd)@digits[0-9]+(a)(xy))/

都喜欢搞成语义化了

回到顶部