标签: 正则

  • JavaScript 中使用正则 `u` 标记匹配多语言

    JavaScript 中使用正则 `u` 标记匹配多语言

    JavaScript 里使用 Unicode 编码字符串。Unicode 是一种可变长度的编码类型,大部分时候,它用两个字节表示一个字符,大部分常见字符都在这 65536 的范围内。一些少见字符,比如各种语言文字、emoji,则会用到 4 个字节。

    以前我们用正则校验字符串的时候,可以用 /[a-zA-Z0-9]/ 检查字符,这样对英文和数字没问题,但不能匹配中文。如果要匹配中文和中文标点,可以用:

    /[\u4E00-\u9FCC\u3002\uff1b\uff0c\uff1a\u201c\u201d\uff08\uff09\u3001\uff1f\u300a\u300b]+/g

    ES2018 之后,我们可以使用 Unicode 属性 \p{...} 来匹配某一个类型的字符串,配合 /u 标记,就可以方便地匹配多字节字符串了。

    在匹配多语言文字时,可以传入 Script 参数,达到非常高效且简便的写法。比如中文,就是 \p{sc=Han},上面那么长的穷举(其实还没举完)正则只需要这么几个简单的字符就能替换,简单多了,对吧?借用下别人的例子:

    let regexp = /\p{sc=Han}/gu; // returns Chinese hieroglyphs
    
    let str = `Hello Привет 你好 123_456`;
    
    alert( str.match(regexp) ); // 你,好

    我们还可以用这个属性来匹配俄文:\p{sc=Cyrillic}。不过有趣的是,欧洲诸国文字多少有些区别,除了我们最熟悉的英文 26 个字母,德文就有 üöä,法文也有 ù,但它们都是拉丁文, \p{sc=Latin},甚至土耳其文也是,只有俄文不一样。

    Babel 也包含了对应的插件:@babel/plugin-proposal-unicode-property-regex · Babel (babeljs.io),在古早浏览器里可以转换成非 Unicode 形态,所以基本上可以放心使用。

    还是要不断更新自己的知识才行呀。

    (更多…)
  • JavaScript 获取正则表达式中子表达式的个数

    JavaScript 获取正则表达式中子表达式的个数

    正如标题所示,我厂有这么一个需求。我不会,老板鄙视我后丢过来一个链接:stackoverflow: Count the capture groups in a qr regex?

    看不太懂 Perl,但是这个思路很棒。所以改写成 JS 版,并记录如下:

    function countCapturingGroups(r){
      r = new RegExp(`|${r.source}`);
      const result = ''.match(r);
      return result.length - 1;
    }
    
    const result = countCapturingGroups(/fo(.)b(..)/);
    console.log(result); // 2
    

    它的原理是这样的。构建一个新正则,包含两部分:空字符和目标正则。空字符正则会完成与目标字符串的匹配,保证有结果(不然的话就会返回 null。接下来 | 会保证后面的正则也是有效的,可以生成包含子表达式结果的数组。

    我们知道,结果是个类数组,结构大约是:

    1. 全部匹配字符串
    2. 0~N 子表达式结果
    3. 其它一些属性

    所以用其长度 – 1 就能获得子表达式的个数。从功耗上来说,这个应该是很节省了。

  • 正则笔记

    正则笔记

    常看常新的正则教程

    正则表达式30分钟入门教程

    匹配中文及中文标点

    如今建议用 /\p{sc=Han}/gu,参见:JavaScript 中使用正则 `u` 标记匹配多语言。

    const reg = /[\u4E00-\u9FCC\u3002\uff1b\uff0c\uff1a\u201c\u201d\uff08\uff09\u3001\uff1f\u300a\u300b]+/g

    ES2018~ES2019 中的正则

    转大写/小写

    转大写 \U\u,转小写 \L\l。比如 camel case 转 kebab case,可以查找 [A-Z],替换为 \L$0

    IDE 里可以,JS 不行。

    判断行开头或xx

    比如 css 规则中的值,我们可能会用 /[\s:](值表达式)/ 这样的表达式,但它没法匹配行的开头,^ 不能放到 [] 里。此时只需要 (^|[\s:]) 即可。

  • 记一个正则问题

    记一个正则问题

    前几天写代码,遇到一个需求:

    1. 解析 sleep NUMBER 这样的命令
    2. 能够识别缺参数或者参数错误的情况

    这个正则并不复杂,初步写出来大概是这样:sleep\s+(.*)。这样,$1 就是参数,然后就可以检验。但是 .* 匹配“任意字符出现零次或多次”,所以实际测试发现它根本不匹配任何参数。

    然后我就改成了 sleep(?:\s+(.*))?,然后在下一步 trim。这样,sleep 后面整个都是可选参数,就能解决上面的问题。

    然后就被老板骂了……老板的答案是 sleep\s+(.*?)\s*$。重点在于后面的 $,要求正则必须匹配行尾,这样一来,懒惰模式的 .*? 就需要一直匹配到行尾,并且尽量少匹配内容,所以诸如 a b 之类的情况也可以正常跑匹配了。


    从这里,我学到:

    1. $\b 虽然并不能匹配到一个确定的字符,但它们同样意义重大
    2. 不特定长度匹配,包括 *+,甚至 {n, m},在懒惰模式下,后面都要尽量跟上明确的结束条件,以便让前面尽快结束。
  • 给URL加上合理的“/”收尾

    给URL加上合理的“/”收尾

    Astinus 0.2版升级过程中,有一个Feature是这样的:

    小雷的数据源只能识别如“http://www.zol.com.cn/”的请求,前面必须有http,后面必须有“/”。我的目标是无论用户输入什么,都能得到正确的结果。

    开始想的比较简单,直接就这么写了

    function correctURL (str) {
      var tail = url.substr(url.lastIndexOf('/') + 1);
      if (tail.indexOf('?') == -1 && tail.charAt(tail.length - 1) != '/') {
        url += '/';
      }
      return url;
    }

    写这段代码时已过午夜,脑子比较糊涂,次日中午反应过来,赶紧修改,经过反复调试,得到正确结果:

    function correctURL(str) {
      var tail = url.substr(url.lastIndexOf('/') + 1);
      if (tail != '' && tail.match(/\.(s?html?|php|asp)/) == null && tail.charAt(tail.length - 1) != '/') {
        url += '/';
      }
      return url;
    }

    顺便说下,Chrome的JavaScript控制台在调试时真好用。另外,将来也要考虑采用测试驱动的方法来写JS了。