zoukankan      html  css  js  c++  java
  • 谈谈 JavaScript 的正则表达式

    一、背景


    最近在做 CMS 系统中不同身份登录用户的权限管理,涉及到对 api 路径的识别去判断是否放行。以前对正则表达式都是敬而远之,要用到的话都是直接复制粘贴现成网上的表达式,看也看不太懂,借这次机会熟悉下,不求钻的多深,但求有个整体的认知,满足我目前的简单需求即可。

    二、介绍


    正则表达式(Regular Expression)是一种用来匹配字符串的强有力的工具。

    各个编程语言对正则表达式的支持标准有所差异,这里只以 JavaScript 为例。

    三、用法


    本文对 非捕获括号、正向肯定查找、正向否定查找 不做介绍。

    1、匹配符

    匹配符 可以匹配
    Iamstring Iamstring
    . 一个任意字符
    d 一个数字
    D 一个非数字
    [a-zA-Z] 一个字母
    [^a-zA-Z] 一个非字母
    w 一个字母、数字或者下划线
    [0-9a-zA-Z_] 同上
    s 一个空格(还包括制表符、换页符和换行符)
    [u4e00-u9fa5] 中文
    A|B A 或 B
    [ABC] A 或 B 或 C
    [A-C] 同上

    2、特殊符号

    上面的匹配符可以搭配下表的符号,如 /[A-Z]{3}/

    如果遇到歧义可使用**括号 **(),如/bo+/ /(bo)+/表达的意思不一样。

    特殊符号 表示 放匹配符的前面 放匹配符的后面 备注
    {n} n个字符
    {n,m} n-m个字符
    * 0或多个字符 等价于{0,}
    + 至少一个字符 等价于{1,}
    ? 0或1个字符 等价于{0,1}
    ^ 表示行的开头
    $ 表示行的结束
     表示单词的边界
    B 表示单词的非边界

    如果上述特殊符号是普通字符的话,需要用转义,如[A-Z]?{3}不对而[A-Z]?{3}可以正确匹配上 "D???"。

    3、贪婪模式和懒惰模式

    (1)如何设置?

    JavaScript 的正则表达式默认为贪婪模式(匹配尽量多的字符)。

    但如果在 *、 +、? 或 {} 的后面出现?,将会变为非贪婪的懒惰模式(匹配尽量少的字符)。

    例如,对 "123abc" 应用 /d+/ 将会返回 "123",如果使用 /d+?/,那么就只会匹配到 "1"。

    (2)有何区别?

    上图里有个术语叫回溯,会影响到正则匹配字符串的性能,在平时写正则的时候需要注意,详细请看下文的三、性能问题

    注:java 的正则里还有一种模式叫独占模式,貌似 JS 里没有(待求证)。

    4、JavaScript 里使用

    (1)创建和 function
    // *** 创建正则表达式 ***
    
    // 方法一:/正则表达式主体/修饰符(可选)
    var reg1 = /d/;
    // 若 RegExp 是全局模式
    // var reg1 = /d/g;
    
    // 方法二:new RegExp('正则表达式')
    // 注意:不用添加/***/,且由于是字符串,所以需要考虑转义!
    // var reg2 = new RegExp("\d");
    // 若 RegExp 是全局模式
    // var reg2 = new RegExp("\d",'g');
    
    var string = '你好 123 123 js'
    
    // *** RegExp 的方法 - test() exec() *** 
    console.log(reg1.test(string));  // true or false
    
    console.log(reg1.exec(string));  // [ '1', index: 3, input: '你好 123 123 js' ]
    console.log(reg1.exec(string));  // [ '1', index: 3, input: '你好 123 123 js' ] (跟上面一样)
    // 若 RegExp 是全局模式
    console.log(reg1.exec(string));  // [ '1', index: 3, input: '你好 123 123 js' ]  
    console.log(reg1.exec(string));  // [ '2', index: 4, input: '你好 123 123 js' ]
    console.log(reg1.exec(string));  // [ '3', index: 5, input: '你好 123 123 js' ]
    console.log(reg1.exec(string));  // [ '1', index: 7, input: '你好 123 123 js' ]
    console.log(reg1.exec(string));  // [ '2', index: 8, input: '你好 123 123 js' ]
    console.log(reg1.exec(string));  // [ '3', index: 9, input: '你好 123 123 js' ]
    console.log(reg1.exec(string));  // null
    console.log(reg1.exec(string));  // [[ '1', index: 3, input: '你好 123 123 js' ]
    
    // *** String 的方法 - search() match() replace() split()  *** 
    console.log(string.search(reg1));   // 返回第一个匹配的下标,没有即是 -1 
    
    console.log(string.match(reg1));    // [ '1', index: 3, input: '你好 123 123 js' ] (跟RegExp.exec()一样)
    // 若 RegExp 是全局模式
    console.log(string.match(reg1));    // [ '1', '2', '3', '1', '2', '3' ]
    
    console.log(string.replace(reg1,'替换'));   // "你好 替换23 123 js" 
    // 若 RegExp 是全局模式
    console.log(string.replace(reg1,'$2-$1'));   // "你好 替换替换替换 替换替换替换 js" 
     
    console.log(string.split(reg1));    // [ '你好 ', '', '', ' ', '', '', ' js' ]
    
    

    除了g表示全局模式,还有i表示忽略大小写,m表示执行多行匹配。

    (2)分组

    捕获括号() 可以分组,受影响的是 match()、split()、replace() 方法。

    var string = '010-12345'
    // 无()
    var reg1 = /^d{3}-d{3,8}$/;
    // 有()
    var reg2 = /^(d{3})-(d{3,8})$/;
    
    // match()
    console.log(string.match(reg1));    
    // [ '010-12345', index: 0, input: '010-12345' ]
    console.log(string.match(reg2));    
    // [ '010-12345', '010', '12345', index: 0, input: '010-12345' ]
    
    // split()
    console.log(string.split(reg1));    
    // [ '', '' ]
    console.log(string.split(reg2));    
    // [ '', '010', '12345', '' ]
    
    // replace() - 注意 $1、$2 的含义
    console.log(string.replace(reg1,'替换'));
    console.log(string.replace(reg2,'$2-$1'));	// 12345-010
    
    

    三、性能问题


    首先,实现正则表达式引擎有两种方式:DFA 自动机(Deterministic Final Automata 确定型有穷自动机)和 NFA 自动机(Non deterministic Finite Automaton 不确定型有穷自动机)。

    对于这两种自动机,他们有各自的区别,这里并不打算深入将它们的原理。简单地说,DFA 自动机的时间复杂度是线性的,更加简单稳定,但是功能有限。而 NFA 的时间复杂度比较不稳定,有时候很好,有时候不怎么好,好不好取决于你写的正则表达式,但是胜在功能更加强大。

    JS 与大多数主流语言的正则引擎选用的都是 NFA。

    但需要注意的是,这种正则表达式引擎在进行字符匹配时会发生回溯(backtracking)。而一旦发生回溯,那其消耗的时间就会变得很长,有可能是几分钟,也有可能是几个小时,时间长短取决于回溯的次数和复杂度。

    这里有两篇关于 JAVA 中遭遇到由于正则表达式的糟糕性能导致上线后服务器 CPU 飙到 100% 的血的经验地分享:

    https://zhuanlan.zhihu.com/p/38278481

    http://www.cnblogs.com/study-everyday/p/7426862.html

    所以,我们更要在平时写正则表达式的时候,更加注重性能,避免回溯机制带来的隐患。我们可以用 https://regex101.com/ 来测试下match 的时间如何。

    四、推荐工具


    1、在线匹配

    http://tool.oschina.net/regex

    2、查看正则表达式的具体解析过程

    https://regexper.com

    五、参考资料:


    1、MDN教程

    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions

    2、廖雪峰教程

    https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001434499503920bb7b42ff6627420da2ceae4babf6c4f2000

  • 相关阅读:
    asp.net 数据导出到Excel
    系统架构之畅想
    access2003绿色中文版(转)
    项目那点事(共享)
    不错的消息提示
    配置Windows media service 流媒体服务器
    MemberShip 数据库的安装,web.config的配置及数据库文档结构
    windows server 2003 service pack 2 无法安装...产品密钥可能无效
    残忍,人
    js小技巧鼠标滑过显示大图
  • 原文地址:https://www.cnblogs.com/xjnotxj/p/10113394.html
Copyright © 2011-2022 走看看