美文网首页
JS正则表达式

JS正则表达式

作者: Leonard被注册了 | 来源:发表于2019-11-11 23:07 被阅读0次

    regular expression : RegExp
    用来处理字符串的规则

    • 只能处理字符串
    • 它是一个规则:可以验证字符串是否符合某个规则(test),也可以把字符串中符合规则的内容捕获到(exec/match...)
    let str = "you can you up, no can no bb"
    let reg = /\d+/
    reg.test(str);    // false
    
    str = "123-456"
    reg.exec(str);  // ["123", index: 0, input: "123-456", groups: undefined]
    

    编写正则表达式

    两种创建方式

    // 字面量方式
    let reg = /\d+/;
    
    // 构造函数模式创建
    let reg = new RegExp("\\d+");
    

    两者区别:
    1.构造函数中传递的是字符串,所以\需要写两个才能代表斜杠
    2.两个斜杠中间包起来的都是元字符,如果正则中要包含某个变量的值,则不能用字面量创建的方式创建,而应转用构造函数模式

    let name = "leonard";
    reg = /^@"+name+"@$/;
    reg.test("@leonard@");          // false
    reg.test('@""""nameeeee"@');    // true
    reg = new RegExp("^@"+name+"@$");
    reg.test("@leonard@");          // true
    

    正则表达式由两部分组成

    • 元字符
    • 修饰符
    /* 常用元字符 */
    // 量词元字符:设置出现的次数
    * 零到多次
    + 一到多次
    ?零次或一次
    {n} 出现n次
    {n,} 出现n到多次
    {n,m} 出现n到m次
    
    // 特殊元字符:单个或者组合在一起代表特殊的含义
    \  转义字符
    .  除\n外的任意字符
    ^  以哪一个元字符作为开始
    $  以哪一个元字符作为结束
    \n  换行符
    \d  0~9之间的一个数字
    \D  非0~9之间的一个数字
    \w  数字、字母、下划线中的任意一个字符
    \s  一个空白字符(包含空格、制表符、换页符等)
    \t  一个制表符(一个TAB键:四个空格)
    \b  匹配一个单词的边界
    x | y  x或者y中的一个字符
    [xyz]  x或者y或者z中的一个字符
    [^xy]  除了x/y以外的任意字符
    [a-z]  指定a-z这个范围中的任意字符
    [^a-z]  除了a-z这个范围外的任意字符
    ()  正则中的分组符号
    (?:)  只匹配不捕获
    (?=)  正向预查
    (?!)  负向预查
    
    // 普通元字符:代表本身含义的
    /leonard/  匹配的就是字符串"leonard"
    
    /* 常用修饰符 : img */
    i => ignoreCase  忽略单词大小写匹配
    m => multiline    可以进行多行匹配
    g => global       全局匹配
    

    元字符详解

    • ^$
    let reg = /^\d/;
    reg.test("leonard");      // false
    reg.test("2333leonard");  // true
    reg.test("leonard2333");  // false
    
    let reg = /\d$/;
    reg.test("leonard");      // false
    reg.test("2333leonard");  // false
    reg.test("leonard2333");  // true
    
    let reg = /^1\d{10}$/;
    reg.test("1234567890");    // false
    
    • \
    let reg = /^2.3$/;
    let reg1 = /^2\.3$/;
    reg.test("2.3");     // true
    reg.test("2@3");     // true
    reg1.test("2.3");    // true
    reg1.test("2@3");    // false
    
    • x | y
    let reg = /^(18|29)$/    // ()处理优先级问题
    reg.test("18");     // true
    reg.test("18");     // true
    reg.test("129");    // false
    reg.test("189");    // false
    
    • []
    let reg = /^[@+]$/;  // 中括号中出现的字符一般都代表本身的含义
    reg.test("@@");    // false
    reg.test("@+");    // false
    reg.test("@");     // true
    
    reg = /^[\d]$/;   // \d在中括号中还是0-9
    reg.test("d");    // false
    reg.test("\\");   // false
    reg.test("9");    // true
    
    reg = /^[10-29]$/;    // 中括号中不存在多位数
    reg.test("1");      // true
    reg.test("9");      // true
    reg.test("0");      // true
    reg.test("10");     // false
    

    常用的正则表达式
    1.验证是否为有效数字
    let reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/
    2.验证6~16密码,含数字、字母、下划线
    let reg = /^\w{6,16}$/
    3.验证真实姓名(2~10位)
    let reg = /^[\u4E00-\u9FA5]{2,10}(·[\u4E00-\u9FA5]{2,10}){0,2}$/
    4.验证邮箱
    let reg = /^\w+((-\w+)|(\.\w+))*@[a-zA-Z0-9]+((\.|-)[a-zA-Z0-9]+)*\.[a-zA-Z0-9]+$/
    5.身份证号码
    let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/


    正则的捕获

    实现正则捕获的方法

    • RegExp.prototype上的方法
      • exec
        基于exec实现正则的捕获
        1.捕获到的结果是null或者一个数组
        首项:本次捕获到的内容
        其余项:对应小组本次单独捕获的内容
        index:当前捕获内容在字符串中的起始索引
        input:原始字符串
        2.每执行一次exec只能捕获到一个符合正则规则的
        正则捕获的懒惰性:由于默认情况下lastIndex的值不会被修改,所以每次执行只捕获第一个
        解决办法:全局修饰符g,全部捕获后会出现循环捕获现象
    let str = "leonard2333age18"
    let reg = /\d+/;
    reg.exec(str);    // ["2333" ...]
    reg.exec(str);    // ["2333" ...]
    reg.exec(str);    // ["2333" ...]
    let reg1 = /\d+/g;
    reg1.exec(str);    // ["2333" ...]
    reg1.exec(str);    // ["18" ...]
    reg1.exec(str);    // null
    reg1.exec(str);    // ["2333" ...]
    
    • test
    let str = "{0}年{1}月{2}日";
    let reg = /\{(\d+)\}/g;
    console.log(reg.test(str));    // true
    console.log(RegExp.$1);        // 0
    console.log(reg.test(str));    // true
    console.log(RegExp.$1);        // 1
    console.log(reg.test(str));    // true
    console.log(RegExp.$1);        // 2
    console.log(reg.test(str));    // false
    console.log(RegExp.$1);        // 2,存储上一次捕获的结果
    
    • 字符串String.prototype上支持正则表达式处理的方法
      • replace
    let time = "2019-8-8";
    let reg = /(\d{4})-(\d{1,2})-(\d{1,2})/g;
    console.log(time.replace(reg,"$1年$2月$3日"));    // "2019年8月8日"
    
    time = time.replace(reg,(...args)=>{
        let [,$1,$2,$3] = args;
        return `${$1}年${$2}月${$3}日`    // 返回内容替换匹配内容
    })
    console.log(time);    // "2019年8月8日"
    
    // 利用replace实现单词首字母大写
    let str = "how are you?";
    let reg1 = /\b([a-zA-Z])[a-zA-Z]*\b/g;
    str = str.replace(reg1, (...args)=>{
      let [content, $1] = args;
      $1 = $1.toUpperCase();
      content = content.substring(1);
      return $1 + content;
    })
    console.log(str);    // "How Are You?"
    
    • match
    let str = "leonard2333leonard123leonard333";
    let reg = /\d+/g;
    console.log(str.match(reg));    // ["2333", "123", "333"]
    
    • splite

    基于exec实现一次全匹配的方法execAll

    ~ function(){
      function execAll(str = ""){
        if(!this.global) return this.exec(str);
        let ary = [],
            res = this.exec(str);
        while(res){
          ary.push(res[0]);
          res = this.exec(str);
        }
        return ary.length === 0 ? null : ary;
      }
      RegExp.prototype.execAll = execAll;
    }()
    
    let str = "leonard2333leonard123leonard333";
    let reg = /\d+/g;
    console.log(reg.execAll(str));    // ["2333", "123", "333"]
    

    分组捕获

    let str = "350623198401234423";
    let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/;
    let reg1 = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|X)$/;    // 只匹配不捕获
    reg.exec(str);
    str.match(reg);
    // ["350623198401234423", "350623", "1984", "01", "23", "2", "3"...]
    reg1.exec(str);
    str.match(reg1);
    // ["350623198401234423", "350623", "1984", "01", "23", "2"...]
    

    分组引用

    let str = "tooth";
    let reg = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z][a-zA-Z]$/;
    console.log(reg.test(str));    // true
    

    正则捕获的贪婪性

    let str = "啊喽哈2333@3222";
    // 正则捕获的贪婪性:默认情况下,正则捕获的时候,是按照当前正则所匹配的最长结果来获取的
    let reg = /\d+/g;
    console.log(str.match(reg));  //  ["2333","3222"]
    // 在量词元字符后面设置?:取消捕获的贪婪性
    reg = /\d+?/g;
    console.log(str.match(reg));  //  ["2", "3", "3", "3", "3", "2", "2", "2"]
    

    问号在正则中的五大作用

    • 问号左边是非量词元字符:代表量词元字符出现零到一次
    • 问号左边是量词元字符:取消捕获时的贪婪性
    • (?:) 只匹配不捕获
    • (?=) 正向预查
    • (?!) 负向预查

    正向预查和负向预查

    • (?=pattern) 正向预查
      在任何匹配pattern的字符串开始处匹配字符串,是非获取字符串(即只匹配不获取)
    var str = "leonard111leonard2333leonard233leonard666"
    var reg = /leonard(?=23+)/g;
    var a = str.match(reg);
    console.log(a); // ["leonard", "leonard"]
    
    • (?!pattern) 负向预查
      匹配方式与正向相反,返回的是匹配不到的字符串,也是非获取字符串
    var str = "leonard111leonard2333leonard233leonard666"
    var reg = /leonard(?!1+)/g;
    var a = str.match(reg);
    console.log(a); // ["leonard", "leonard", "leonard"]
    
    • 曾经面过的一道题
      验证6-16位的密码,可以是数字、字母、下划线,但要求必须包含大小写字母、数字三项
      正向预查写法:
      let reg = /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])\w{6,16}$/g
      负向预查写法:
      let reg = /^(?![0-9]+$)(?![A-Z]+$)(?![a-z]+$)(?![0-9a-z]+$)(?![0-9A-Z]+$)(?![a-zA-Z]+$)\w{6,16}$/g

    面试题
    获取出现次数最多的字母

    // Method 1
    let str = "let me put it this way";
    let obj = {};
    
    [].slice.call(str).forEach(item=>{
      if(item === ' ') return;
      if(typeof obj[item] !== 'undefined'){
        obj[item]++;
        return;
      }
      obj[item] = 1;
    })
    
    let max = 1,
        res = [];
    for(let key in obj){
      max < obj[key] ? max = obj[key] : null;
    }
    for(let key in obj){
      max === obj[key] ? res.push(key) : null;
    }
    console.log(`出现最多次的字母是${res.join()},总共出现了${max}次`)
    
    // Method 2
    let str = "let me put it this way";
    let obj = {};
    str = str.replace(/\s/g,() => '').split('').sort((a,b) => a.localeCompare(b)).join('');
    
    let reg = /([a-zA-Z])\1*/g;
    let ary = str.match(reg).sort((a,b) => b.length - a.length);
    
    let res = ary.slice(0,1);
    let max = res[0].length;
    for(let i = 1; i < ary.length; i++){
      if(ary[0].length !== ary[i].length) break;
      res.push(ary[i])
    }
    res = res.map((item) => {
      return item.substr(0,1);
    })
    console.log(`出现最多次的字母是${res.join()},总共出现了${max}次`);
    
    

    相关文章

      网友评论

          本文标题:JS正则表达式

          本文链接:https://www.haomeiwen.com/subject/utnibctx.html