JavaScript初级篇——正则表达式

175 阅读8分钟

使用正则,我们可以进行字符串的查找、替换、验证、分割

一、正则表达式创建方式

有两种创建方式:字面量创建方式构造函数创建方式

1.1、字面量创建方式

  • 使用/.../的方式创建
  • 其中不能接收变量
let str = 'abc123abc123aakjsdh15';    
let reg = /abc/g;
console.log(str.match(reg)); // ['abc', 'abc']

! 以上示例中的/abc/g代表全局匹配字符串"abc"

!字面量创建方式不能接收变量:

let str = 'abc123abc123aakjsdh15';   
let abc = '123';
let reg = /abc/g;
console.log(str.match(reg)); // ['abc', 'abc'] 依旧表示全局匹配"abc"而不是"123"

1.2、构造函数创建方式

  • 语法:let reg = new RegExp('表达式', 匹配模式)
  • 遇到'\'要转义为'\\'
  • 可接收变量
let str = 'abc123abc123aakjsdh15';
let reg = new RegExp('\\d', 'g'); // 表示全局匹配数字(\d表示数字,在表达式中\要转义为\\)g表示全局匹配
console.log(str.match(reg)); // ['1', '2', '3', '1', '2', '3', '1', '5']

二、正则匹配方法

2.1、正则对象相关方法

2.1.1、test

测试该字符串是否符合正则表达式,返回true/false

  let str = '123asd890sdd1';
  let reg= /\d+/g; // 表示匹配全局是1个或多个数字
  console.log(reg.test(str)); // true
2.1.2、exec
  1. 执行后返回匹配结果(存在则返回结果,不存在则返回null)
  2. 执行结果与reg.lastIndex(下一次匹配的起始位置)有关
  1. 如果第一次执行,那么就返回第一次匹配结果
  2. 再次执行会从剩余字符串中进行匹配
  1. 直到匹配完毕返回null..
 let str = '123asd890sdd1';
 let reg1 = /\d+/g;
  /*
    [0: "123"
    groups: undefined
    index: 0
    input: "123asd890sdd1"
    length: 1]
  */
  console.log(reg1.exec(str));
  console.log(reg1.lastIndex);// 3
  /*
  [0: "890"
  groups: undefined
  index: 6
  input: "123asd890sdd1"
  length: 1]
  */
  console.log(reg1.exec(str));
  console.log(reg1.lastIndex);// 9
  /*[0: "1"
    groups: undefined
    index: 12
    input: "123asd890sdd1"
    length: 1]
  */
  console.log(reg1.exec(str));
  console.log(reg1.lastIndex);// 13
  console.log(reg1.exec(str)); // null
  console.log(reg1.lastIndex);// 0

!注意 如果执行了 reg.test后,再执行reg.exec,就会从第一次匹配的结果往后去匹配

  let str = '123asd890sdd1';
  let reg1 = /\d+/g;
  console.log(reg1.test(str));
  console.log(reg1.exec(str));// ['890', index: 6, input: '123asd890sdd1', groups: undefined]
  console.log(reg1.lastIndex);// 9
  console.log(reg1.exec(str));// ['1', index: 12, input: '123asd890sdd1', groups: undefined]
  console.log(reg1.lastIndex);// 13
  console.log(reg1.exec(str));// null
  console.log(reg1.lastIndex);// 0

2.2、字符串相关方法

2.2.1、split

以传递的正则所匹配的内容进行字符串的分割

let str = '123asd890sdd1';
console.log(str.split(/\d+/));// ["", "asd", "sdd", ""]
console.log(str.split(/\d/));// ['', '', '', 'asd', '', '', 'sdd', '']
// 要注意!开头已经是数字了会分割出来一个空字符串,
// 结尾也是数字,也会有一个空字符串

2.2.2、search

查找符合正则的第一个匹配内容的索引

  let str = '123asd890sdd1';
  console.log(str.search(/\D+/g));// 3 匹配第一个非数字内容的索引

2.2.3、match

  • 如果是非全局匹配,显示的就是像exec似的,显示一组详细信息
  • 如果是全局匹配,会返回一个数组
  • 未找到则返回null
  let str = '123asd123sdd1';
  let reg2 = /123/
  console.log(str.match(reg2)); // ['123', index: 0, input: '123asd123sdd1', groups: undefined]

  let reg3 = /123/g
  console.log(str.match(reg3));// ['123', '123']

2.2.4、replace

  1. replace第一个参数可以接收一个正则表达式,去替换正则表达式所匹配到的内容
  2. 返回替换后的新字符串,不会影响原来字符串
  3. 第二个参数除了传递目标字符串外,还可以传递一个回调函数
  • 回调函数接收三个值:
    • 第一个值表示匹配到的字符串
    • 第二个值表示分组
    • 第三个值表示索引
  let str = '123asd123sdd1';
  let reg4 = /\d/g;
  console.log(str.replace(reg4, '*')); // ***asd***sdd*

  let result = str.replace(reg4, function(arg){
    return '*';
  })
  console.log(result);// ***asd***sdd*

三、元字符

3-1、字符相关

  let str = '123_';
  let reg = /\w+/g;
  console.log(reg.test(str));// true
  let reg2 = /\W+/g;
  console.log(reg2.test(str));// false
  let str2 = '123';
  let reg3 = /\D+/g;
  console.log(reg3.test(str2)); // false
  let reg4 = /./;
  console.log(reg4.test(str2));// true
  console.log(reg4.test('\u2028')); // false
  let str3 = `a
  b`;
  let reg5 = /a.b/; // 匹配a+非回车换行符号+b,模板字符串会保留换行,可见不会匹配到a.b
  console.log(reg5.test(str3));// false

3-2、数量相关

let str4 = 'abcdeeeffg';
let reg6 = /cde{3}/g;
// 匹配cdeee(3个e)
console.log(reg6.test(str4)); // true
let reg7 = /cde{1,4}/g; // 出现1~4次都可以
console.log(reg7.test(str4));// true
let reg8 = /cde{1,}f/;
console.log(reg8.test(str4)); // true
let str5 = 'my name is lilei';
let reg9 = /my\s+name/g; // 表示my后面匹配一次或无限多次空格然后后面跟name
console.log(reg9.test(str5)); // true

其中,?还可以用来将贪婪匹配改正惰性匹配:

  • 贪婪匹配

即:按照最多的数量去匹配

  • 惰性匹配

即:按照最少的数量去匹配

比如:我们想匹配最少两个数字,最多4个

  let str = '123456789';
  let reg1 = /\d{2,4}/g;
  console.log(str.match(reg1)); // ['1234', '5678']
  let reg2 = /\d{2,4}?/g;
  console.log(str.match(reg2));//  ['12','34','56','78']

3-3、位置相关

  let str6 = '123basdzx';
  let reg10 = /^\w/g;
  // 将开头的第一个字母数字或下划线换成&,如果开头不是字母数字或下划线则不替换
  let ress = str6.replace(reg10, '&');
  console.log(ress);//&23basdzx
  let reg11 = /\w$/g;
  // 将结尾的最后一个字母数字或下划线换成&, 如果结尾不是字母数字或下划线则不替换
  let resss = str6.replace(reg11, '&');
  console.log(resss);//123basdz&

  let str7 = 'this is a book';
  // 只匹配前后都是空格的is
  let reg14 = /\bis\b/g;
  let ressss = str7.match(reg14);
  console.log(ressss);// ['is']
  
  // 匹配左边非边界右边有边界的2个长度的字母数字或下划线
  let reg15 = /\B\w{2}\b/g;
  let res1 = str7.match(reg15);
  console.log(res1);// ["is", "ok"]

3-4、括号相关

3-4-1、()的使用
  • 使用()分组:
    • 使用()分组后,后面再加{n}数量,则表示有几组该内容
let str = 'abababfdsafsa';
// 匹配三次'ab', 如果不加括号则是匹配abbb
let reg = /(ab){3}/g;
console.log(reg.test(str)); // true
  • 使用()提取值:

    • 使用()分组后,可结合字符串的match方法来进行内容提取,提取内容的方式有2种:
      • str.match(reg)[index]
      • RegExp.$n
let str2 = '2021-12-21';
let reg2 = /(\d{4})-(\d{2})-(\d{2})/;
/*
    0: "2021-12-21"
    1: "2021"
    2: "12"
    3: "21"
    groups: undefined
    index: 0
    input: "2021-12-21"
  */
console.log(str2.match(reg2));
console.log(str2.match(reg2)[1]); //2021
// 也可以用RegExp中的$1 $2 ... 来提取值
console.log(RegExp.$1);// 2021
console.log(RegExp.$2);// 12
  • 使用()来替换内容:
    • 使用()分组后,可结合字符串的replace方法进行替换,其中有两种替换方式:

      • str.replace(reg, "$n") $n表示匹配到的分组内容,n表示第几组
      • str.replace(reg, function(arg, val1,val2, ...)) val1,val2...分别表示匹配到的第几组的内容
let str2 = '2021-12-21';
let reg2 = /(\d{4})-(\d{2})-(\d{2})/;

// 用分组来替换成指定字符串
let res = str2.replace(reg2, '$2/$3/$1');
console.log(res); // 12/21/2021

//也可以用回调函数的形式
let ress = str2.replace(reg2,function(arg, year,month,date){
  return `${month}/${date}/${year}`;
})
console.log(ress);// 12/21/2021
  • 使用()来反向引用:
let className = 'news_container-nav';
// 比如我们要匹配中间的链接符号必须统一为_或-要保持一致
// 此时我们就要使用反向引用,比如前面匹配到的是_后面也要用_链接
let reg3 = /\w{4}(_|-)\w{9}(\1)\w{3}/;
// \n表示反向引用第几组的匹配结果 \1则表示反向引用第一组的匹配结果
console.log(reg3.test(className)); // false
3-4-2、[]的使用
  • 使用[]表示或者
    • 比如,[Ll]表示内容是L或者l都可以,也可以使用(L|l)表示
    • ()中表示“或者”需要在内容中加上“|”,而[]表示或者就不需要加“|”
let strs = 'My name is LiLei';
// 匹配Lilei中的lei的l可以是 L也可以是l
// let regg = /Li(L|l)ei/g; // 可以使用小括号
let regg = /Li[Ll]ei/g; // 在中括号里就不用加|了,本来就代表或者的关系
console.log(regg.test(strs)); // true
  • 使用[]表示一段内容

    • []表示一段内容时,其中的^表示“非”的意思,而不是“开头”的意思
  let strs = 'My name is LiLei';
  let regs = /[^0-9]/g; // 非数字
  console.log(regs.test(strs)); // true
3-4-2、{}的使用

详见3-2

3-5、匹配模式

  • 在使用多行匹配m时,要同时加上g
let str = `abc
efg
hij`;
let reg = /^\w/gm; // 如果多行匹配开头,注意不要忘了g!!
let res = str.replace(reg, '*');
/*
*bc
*fg
*ij
*/
console.log(res);
  • s可以让'.'匹配到换行

    • 由于'.'会匹配非换行符、非回车符、非段落结束符和非行结束符,所以要想让'.'匹配到换行符,就要加上s
let str = `abc
efg
hij`;
// let reg2 = /^a.*j$/g; // 由于.表示非换行 非回车 非段落结束符 非行结束符,所以这样就匹配不到了,因为换行了
let reg2 = /^a.*j$/gs; // a开头j结尾,中间任意多个非..符号,但是忽略换行符
console.log(reg2.test(str)); // true
  • 使用u进行匹配unicode编码时,要配合\u与{}使用
    • 当\u与{}连用时,{}中的数字表示unicode编码
let str3 = 'a';
// a的unicode编码是61,{}与\u连用,{}中的数字表示unicode编码值
let reg3 = /\u{61}/gu; // 加上u表示匹配unicode编码
console.log(reg3.test(str3)); // true
  • 使用y开启粘性模式

    • 开启粘性模式后,匹配到的内容必须是连续的
let ss = '12asd123';
let regg = /\d/gy;
console.log(regg.exec(ss));// ['1', index: 0, input: '12asd123', groups: undefined]
console.log(regg.exec(ss));// ['2', index: 1, input: '12asd123', groups: undefined]
// 如果不开启粘性模式,这里输出1
console.log(regg.exec(ss)); // null 因为开启了粘性模式,必须是连续的,所以执行到第三次为字母了就输出了null

四、命名分组

利用()分组后,可使用?<名称>来给分组起名

  let str = '2021-12-21';
  let reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;

  /*
  0: "2021-12-21"
  1: "2021"
  2: "12"
  3: "21"
  groups:
    day: "21"
    month: "12"
    year: "2021"
  index: 0
  input: "2021-12-21"
  */
  // 可见,使用了命名分组后,groups中就拥有了命名和对应的值
  console.log(str.match(reg));
  console.log(str.match(reg).groups.day); // 21

五、零宽断言

5-1、正向零宽断言

let ss = 'iphone3iphone4iphone5iphonenumber';
// 希望匹配到iphone+数字,但是又希望替换iphone为“苹果”,又不希望替换数字
// 肯定的。匹配iphone后面是数字的
let regss = /iphone(?=\d)/g;
// 否定的。匹配iphone后面不是数字的
let regsss = /iphone(?!\d)/g;
console.log(ss.replace(regss,'苹果'));// 苹果3苹果4苹果5iphonenumber
console.log(ss.replace(regsss, '苹果'));// iphone3iphone4iphone5苹果number

5-2、负向零宽断言

let sss = '10px20px30pxipx';
// 肯定的。希望把px换成‘像素’,而数字不动
let gg = /(?<=\d+)px/g;
// 否定的。希望把px换成‘像素’,而前面是非数字的
let ggg = /(?<!\d+)px/g;
console.log(sss.replace(gg,'像素')); // 10像素20像素30像素ipx
console.log(sss.replace(ggg,'像素')); // 10px20px30pxi像素