【JS】正则中的预查、功能强大的 replace 捕获和替换,自我思考和总结

1,124 阅读6分钟

正负向预查

(?=) => 正向预查

(?!) => 负向预查

// ?: => 只匹配不捕获

//这个正则的意思的zhufeng后面必须紧跟的是peixun,中间也不能有任何东西,才可以匹配成功
let  reg = /zhufeng(?=peixun)/;
console.log(reg.test('zhufeng'));//false
console.log(reg.test('zhufengpeixun'));//true
console.log(reg.test('zhufeng132465peixun'));//false
//这个正则的意思的zhufeng后面必须不能紧跟的是peixun,才可以匹配成功
let  reg = /zhufeng(?!peixun)/;
console.log(reg.test('zhufeng'));//true
console.log(reg.test('zhufengpeixun'));//false
console.log(reg.test('zhufeng132465peixun'));//true

创建正则的方式

1.字面量的方式

2.构造函数的方式

利用构造函数的方式去创建正则的时候传递的实参是字符串

let reg = new RegExp('wer');
let reg1 = new RegExp('\\d+');
//注意字符串里面的反斜杠有特殊的意义,如果要用构造函数的方式创建正则,需要在里面加两个反斜杠表示的是一个反斜杠的意思

//利用构造函数创建的正则可以传递变量
//字面量的方式创建的正则里面是不可以加变量的
let type = "erYa";
let reg = new RegExp('^'+ type +'$');
console.log(reg);//=> /^erYa$/
let reg1 = new RegExp(`^${type}$`);
console.log(reg1);//=> /^erYa$/
//使用字符串拼接或者模板字符串的方式都可以在构造函数创建正则的时候加入变量传递,符合字符串的操作方法

想往正则里面写变量,只能利用构造函数的方式创建正则

replace配合正则使用

replace的三种使用方法

  1. replace(str,str)

  2. replace(reg,str)

  3. replace(reg,fun)

 // 正则捕获之replace 是字符串中实现替换的方法
    let str = 'zhufeng2019zhufeng2018';
    // // 我想把以上字符串中的zhufeng替换成珠峰
     console.log(str.replace('zhufeng','珠峰').replace('zhufeng','珠峰')); // 珠峰2019珠峰2018

    console.log(str.replace(/zhufeng/g,'珠峰')); //  珠峰2019珠峰2018

1.正则在当前字符串中匹配几次,那当前的回调函数就会执行几次

2.当前匹配之后会进行捕获,还会把当前捕获的内容传递给当前正在执行的函数,每一次匹配捕获得到的都是一个数组,数组的第一项是大正则匹配的,后面的都是小分组匹配的,将这个数组作为参数传递给回调函数

3.当前函数返回啥,就会把当前大正则匹配的地方替换成啥,即return的是啥,就替换成啥,不写return就返回undefined

    let str = 'zhufeng2019zhufeng2018'
    // 把zhufeng替换成zhufengpeixun
    // console.log(str.replace('zhufeng','zhufengpeixun').replace('zhufeng','zhufengpeixun'));//一直匹配第一个,无法实现需求

//搭配正则进行匹配和替换,可以进行全局替换 console.log(str.replace(/zhufeng/g,'zhufengpeixun'));
   
  let res = str.replace(/zhufeng/g,function(){
       console.log(arguments);
        return 'zhufengpeixun'
    })
    console.log(res);

    // 'zhufengpeixun2019zhufengpeixun2018'

几个小案例

//时间格式化(只有年月日的情况)
    let time = '2018-8-12';
    //==> '2018年08月12日'
    let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/g;
    //运用剩余运算符接收捕获到的内容作为参数
    time = time.replace(reg, (...ary)=> {
       console.log(ary);
       let [,$1,$2,$3] = ary; //解构赋值
       $2.length<2? $2 = '0'+$2:null; // 补零
       $3.length<2? $3 = '0'+$3:null; // 补零

       return `${$1}${$2}${$3}日`
     })
     console.log(time);

    console.log(time.replace(reg,'$1年$2月$3日'));

    //--------------------------------------------------------------------------------
    // 单词首字母大写
     let str = 'good good study, day day up!'
     // 把每一个单词的首字母进行大写
     let reg = /\b([a-zA-Z])([a-zA-Z]*)\b/g;//第一种解决方法
     
     //   reg = /\b([a-zA-Z])[a-zA-Z]*\b/g;//第二种解决方法


     str = str.replace(reg,(...ary)=>{
       console.log(ary);
      // 得到每一个单词的首字母,然后把它转大写,在把当前的大写字母和剩下的单词拼接起来返回出去
      //得到每一个单词的首字母的方法有两种,一种是在写正则的时候将首字母单独放入一个分组,后面的字母放入另一个分组
      //另一种方法是一种是在写正则的时候只将首字母单独放入一个分组
       let [$1,$2,$3] = ary; //(第一种方法) $1是当前的单词 $2是当前的首字母 $3就是除了首字母剩下的单词
       $2 = $2.toUpperCase(); // 把首字母转大写
       // 'good'==>'ood'

    //  return $2 + $1.slice(1) 
    //(第二种方法)除首字母以外的字母通过slice方法截取到 然后拼接 
    //==> 'Good'
    
       return $2 + $3; //(第一种方法)
    });
     console.log(str);
    // 'good good study, day day up!'

    //--------------------------------------------------------------------------------
     let s = 100
     let str = 'a1sd1fgh1jk1l';
     // ==> '2018年08月12日'
     let reg = /[a-z](\d)[a-z]/g;
     console.log(str.replace(reg,`{$3}${s}`));
    // '{}{}g{}{}'

     for(var ss of str.matchAll(reg)){
       console.log(ss);
     }

利用replace封装queryUrlparams

 // 利用replace封装queryUrlparams

//第一种是原始解决的方案:利用字符串截取封装queryUrlparams
     let url = 'https:www.baidu.com:5500?name=erYa&age=18#index';

     function queryUrlParams(){
      // this-->当前url
       let paramsStr = this.split('?')[1];
      // 'name=erYa&age=18#index'
       let params = paramsStr.split('#');
       // ['name=erYa&age=18','index']
       let paramsAry = params[0].split('&');
      // ['name=erYa','age=18']
       let obj = {};
      for(var i =0;i<paramsAry.length;i++){
         let item = paramsAry[i]; // 代表数组的每一项 'name=erYa'
         let itemAry = item.split('='); // ['name','erYa']
         obj[itemAry[0]] = itemAry[1];
      }
       obj['hash'] = params[1];
       return obj;
     }
     String.prototype.queryUrlParams = queryUrlParams;
     console.log(url.queryUrlParams()); 
    //-------------------------------------------------------------------------------
    //第二种方法:利用正则replace,封装queryUrlparams
    let url = 'https:www.baidu.com:5500?name=erYa&age=18#index';
     function queryUrlParams() {
       // this-->  url
       // ^ 在中括号中表示除了 这些字符以外的都可以
       let reg = /([^?=&#]+)=([^?=&#]+)/g;
       let obj = {};
       // 单纯利用replace进行捕获,不利用return的特点
       this.replace(reg,(...ary)=>{
         // 从这里给当前obj新增键值对
         let [,key,value] = ary; // 从传递过来的参数中结构出我们想要的值
        obj[key] = value; // 给obj新增键值对
       });
       this.replace(/#([^?=&#]+)/,(...ary)=>{ //利用小分组获得 # 后面的值
         obj['hash'] = ary[1];
       })
      return obj;
    }
     String.prototype.queryUrlParams = queryUrlParams;
     console.log(url.queryUrlParams()); 

//---------------------------------------------------------------------------------------

     function queryUrlParams() {
       let reg = /([^?=&#]+)=([^?=&#]+)/g;
       let obj = {};
       
       this.replace(reg,($1,key,value)=>{obj[key] = value});
       //省略了剩余运算符和解构赋值,直接用形参接收
       
       this.replace(/#([^?=&#]+)/,($1,$2)=>{obj['hash'] = $2});
       return obj;
     }
     console.log(url.queryUrlParams());

时间字符串的格式化

下面的方法中,可以自定义模板,进行替换,什么样式的模板都可以,在replace捕获的时候,只需要利用分组捕获,得到每个位置的索引,通过控制索引就可以得到match捕获到的大正则构成的数组里面的对应的数据,然后用这些数据进行替换,将自定义模板中的对应位置替换即可。

  // 后台返回的时间是 '2018-8-01 '
        // '2018年08月01日 12时12分3秒'
        let time = '2018-8-01 12:12:3'
        
         //可以通过形参赋值默认值的形式给这个模板,如果用户没有传自己自定义的模板就用默认的模板
        function formatTime(template = '{0}年{1}月{2}日 {3}时{4}分{5}秒') {
            let timeAry = this.match(/\d{1,4}/g); // 获取所有的时间
            // console.log(timeAry); // ["2018", "8", "01"]
           //  let template = '{0}年{1}月{2}日 {3}时{4}分{5}秒'; 
           //可以通过形参赋值默认值的形式给这个模板,如果用户没有传自己自定义的模板就用默认的模板
           
            // '2018~08~01'
            template = template.replace(/\{(\d)\}/g, (...ary) => {
                // console.log(ary);
                let [, index] = ary;// 从参数中解构出分组捕获的数字
                let time = timeAry[index] || '00';
                // 如果index获取不到对应的时间,那咱们就给他一个默认值 '00'
                // 把当前捕获的数字当做索引去时间数组里去对应的值
                // console.log(time);
                time.length < 2 ? time = '0' + time : null; // 补零
                return time;
            });
            // '{0}年8月01日 {3}时{4}分{5}秒'
           //  console.log(template);
            return template;
        }
        String.prototype.formatTime = formatTime;
         console.log(time.formatTime('{1}~{2}'));//这里可以传自定义的模板,带上索引即可
        // 时间的模板用户可以自定义,但是要注意模板里的数字,得和年月日时分秒的索引对上
        
/*
如果只是单纯的想要实现时间格式化的功能,而且从后台得到的数据是固定格式的,可以通过replace方法
第一个参数按照后台给的数据,按照格式写好匹配整体的一个大正则,再把里面每个数字的位置利用小分组捕获
在第二个参数,对于小分组捕获到的数字进行字符串拼接或者通过一个回调函数进行处理都可以
利用第二个参数替换掉整体的后台给的数据。
这样只用一次replace就可以实现格式化的目的
(这是个人的思考,得到的方法,仅供参考)
*/        

正则是可以匹配空格的

// 匹配空格
         let reg = /[a-z]( )[a-z]/;
         let str = 'a c'
         console.log(reg);
         console.log(reg.test(str));//true
         console.log(reg.exec(str));

关于正则问题的自我思考总结

首先需要记住常用的元字符和修饰符。
一个中括号表示的只是这个范围内的一位字符
如果一个位置是一个范围的数的话,需要使用小括号进行分组,使用竖线表示或,这样才能规定一个范围的数
如果用到正则里面的关键词需要加转义字符,字符串里面也有一些关键词需要加转义字符,比如 \n

^表示以什么开头  $表示以什么结尾
如果前面只写 ^  就是表示只要以这个开头就行 不管后面是什么
如果需要规定匹配的位数 就必须规定开头和结尾
不考虑位数 就可以不加开头和结尾
/^\d/  匹配的就是0-9开头就可以  不管多少位
/\d$/ 匹配0-9结尾就可以 不管多少位
/\d/ 匹配 只要这个字符串里面有0-9就可以 不管多少位
/^\d$/ 只能是0-9之间的一位数


分析正则问题时
考虑  加g 和 不加g (是否全局匹配)
考虑匹配 还是 捕获  
考虑 是否分组捕获  
考虑分组捕获时 有没有哪个分组是只匹配不捕获的  要加上?:

尽量不要用 exec 、match  matchAll ,这几个都有缺陷
注意捕获时,加g  和  不加g 得到的结果不同
分组捕获时 得到的结果也不同
match不能分组捕获,只能捕获大正则的东西
exec 每次只能捕获到一个符合大正则的 以及 这个大正则里面的小分组的内容,因为具有懒惰性,需要给正则加g,然后还有多次执行才能捕获后面的符合大正则内容和小分组的内容。
matchAll得到的是一个遍历器,搭配 for of 使用,循环遍历,每轮循环得到的都是符合正则的一个大正则和里面的小分组的内容,循环遍历以后得到多组符合正则的一个大正则和里面的小分组的内容,可以将得到的值进行处理,比如先进行解构赋值,分别得到大正则和小分组的内容,然后做一些处理,可以输出

尽量使用replace
replace的功能很强大
第一个参数传正则表达式
第二个参数可以是字符串 或者 是一个回调函数  回调函数的返回值是一个字符串
然后用第二个参数 替换掉 正则捕获到的大正则的部分
一个参数正则表达式 捕获到的 第一个是大正则  后面的都是小分组捕获到的
所以如果
第二个参数可以是字符串,可以通过$1 $2...等等操作小分组 拼接字符串 然后替换大正则捕获的内容
如果
第二个参数是回调函数,回调函数的参数可以直接用形参接收,也可以用剩余运算符接收以后放到一个数组里面,然后如果用形参接收的参数,在回调函数里面就可以直接操作形参,如果用剩余运算符接收的,在回调函数里面就用解构赋值,将小分组的内容解构出来,然后进行处理,最后return字符串,替换大正则捕获的内容