Javascript 中的正则表达式 - 基本介绍及其语法

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 2 天,点击查看活动详情

正则表达式强悍无比,在不同的语言中会有些许差异,本文主要总结在 Javascript 中的正则表达式;

javascript 中的正则表达式在 ECMAScript 3 开始提出,并在后面的规范中得到增强;

你可以通过下面的文章来阅读正则表达式的历史:


1、概述

  1. 正则表达式是用于匹配字符串中字符组合的 模式,由普通字符 和 特殊字符(元字符)组成;

    • 普通字符:包括没有显示指定为元字符的所有可打印和不可打印字符;(eg:大写字母、小写字母、数字、标点符号)

    • 特殊字符(元字符):元字符是正则表达式中的关键字(eg:^、$、*、+、?、.、()、[]、{}、\、|),其具有特殊的含义,如果要把元字符当作普通字符,需要使用转义字符( \ )转义。

  2. 在 JavaScript 中,有正则表达式对象(RegExp);

  3. 正则表达式在 Javascript 中用于正则表达式对象(RegExp) 的 exec()test()方法,和 字符串对象(String)的 match()matchAll()replace()search()split() 等方法;


2、创建一个正则表达式

1、使用正则表达式字面量

const reg = /ab+c/;
const reg = /a/; // 只匹配出现的第一个 a;
const reg = /a/g; // 匹配全部的a
复制代码

正则表达式字面量每一次被使用时都会转换成一个正则对象;


2、使用RegExp 构造函数创建一个正则表达式对象

const reg = new RegExp('a',g);
const reg = new RegExp(/[A-Z]/,i);
复制代码

3、正则表达式的基本语法

修饰符、字符匹配、量词、位置匹配、分支、分组;

1、正则字面量使用//包围

const reg = /daxia/;
复制代码

2、标志、修饰符

在正则表达式字面量中,标志位放在 // 的后面;

  • g:全局搜索
  • i:不区分大小写
  • m:多行匹配,使边界字符^$ 匹配每一行的开头和结尾;
  • s:使正则表达式中的字符圆点 . 中包含换行符 \n
  • u:使用 unicode 码的模式进行匹配;
  • y:执行"粘性"搜索,匹配从目标字符串的当前索引为 lastIndex 的位置开始匹配,匹配失败返回 null;

eg:

// 不区分大小写,并全局匹配 a
const reg = /a/gi;
'abcABC'.match(reg); // ['a', 'A']
复制代码

3、字符

  • .:匹配除换行符(\n\r)之外的所有单字符;

  • []:定义匹配的字符范围;在 []里的 *,+,?,. 等表示的是字符

  • \d:匹配数字 [0-9];

  • \D:匹配非数字 [^0-9];

  • \w:匹配 [0-9a-zA-Z_];

  • \W:匹配 [^0-9a-zA-Z_];

  • \s:匹配一个空白字符;

  • \S:匹配一个非空白字符;

  • 匹配所有字符:[\s\S] 或 [\d\D] 或 [\w\W]

  • \u2028:行分隔符
  • \u2029:段分隔符

eg:

// 匹配任意字符
const reg = /[\s\S]/;
'danid2348*$@R%'.match(reg); // ['d', index: 0, input: 'danid2348*$@R%', groups: undefined]
// 全局匹配任意字符
const reg1 = /[\s\S]/g;
'danid2348*$@R%'.match(reg1); // ['d', 'a', 'n', 'i', 'd', '2', '3', '4', '8', '*', '$', '@', 'R', '%']
复制代码

4、量词

  • *:匹配前面一个表达式0次或多次
  • +:匹配其前面一个表达式1次或多次;
  • ?:匹配前面一个表达式0次或1次,加在 * 和 + 后面可以实现非贪婪匹配;
  • {n}:匹配前面一个表达式 n 次;
  • {n,}:匹配前面一个表达式至少 n 次;
  • {n,m}:匹配前面一个表达式 n 到 m 次;

在量词后面加?就可以实现惰性匹配(eg:+?表示只匹配一次);

eg:

const reg = /\d+/g;
// 全局匹配连续的数字串
'124ddd123'.match(reg); // ['124', '123']
复制代码

5、分支

使用管道操作符来实现分支;

eg:

const reg = /you|boy/g;
// 全局匹配 you 或 boy
'hello, you are a boy'.match(reg); // ['you', 'boy']
复制代码

注:

  • 分支结构是惰性的,匹配到了分支左边的就不会匹配分支右边的;

    const reg = /roo|root/g;
    'root'.match(reg) // ['roo']
    复制代码

6、分组

一个() 里面的内容是一个分组,指定匹配括号里的内容,一个圆括号是一个捕获组,javascript 会在正则对象中存储每一个捕获组匹配到的内容,并且可以在正则表达式中通过 \number 的形式调用;

1、使用构造函数的全局属性获取分组

const str = '123 456';
const reg = /(\d+) (\d+)/g; 
// 使用分组匹配
reg.exec(str); 
// ['123 456', '123', '456', index: 0, input: '123 456', groups: undefined]
RegExp.$1;
// 123
RegExp.$2;
// 234
复制代码

2、在正则表达式内应用分组

const str = "! 456 ! i am daxia";
const reg = /(\!+)[\d\D]+\1/;
// reg 中 \1 代表前面(\!+)匹配到的内容
console.log(reg.exec(str));
// ['! 456 !', '!', index: 0, input: '! 456 ! i am daxia', groups: undefined]
复制代码

3、如果只想使用括号的功能,而不希望括号称为为捕获组,使用非捕获括号 (?:p) 的形式就好了

'ab'.match(/(ab)/); // ['ab', 'ab', index: 0, input: 'ab', groups: undefined]
'ab'.match(/(?:ab)/); // ['ab', index: 0, input: 'ab', groups: undefined]
复制代码

4、其他内容

  1. 括号嵌套时,按树形结构解析分组;

    const reg = /((da)(xia))/;
    reg.exec('daxia is a daxia');
    // ['daxia', 'daxia', 'da', 'xia', index: 0, input: 'daxia is a daxia', groups: undefined]
    复制代码
  2. \10 表示匹配第 10 个分组;

    const reg = /(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)\10/;
    reg.exec('1234567891010'); 
    // ['1234567891010', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', index: 0, input: '1234567891010', groups: undefined]
    复制代码
  3. 使用不存在的分组(分组只到 \3,但是在正则表达式中使用了 \4,匹配的是 \4 对应的字符);

    const reg = /(a)(b)(c)\1\2\3\4/;
    reg.exec('abcabc abcabc4'); 
    // null
    复制代码
  4. 分组后面有量词,那么全局 RegExp 中存储的是最后一次分组匹配的数据;

    const reg = /(\d)+/;
    reg.exec('123 456');
    // ['123', '3', index: 0, input: '123 456', groups: undefined]
    RegExp.$1; // '3'
    复制代码

7、位置匹配词

  • ^:匹配开始,如果在 [] 中使用则表示不匹配方括号里的内容;

    let re = /^a/;
    
    const str = 'bcda'; // null
    const str = 'abcda'; // 可以匹配到
    复制代码
  • $:匹配结尾

  • \b: 匹配一个单词边界,即 \W 与 \w 之间的位置和 \w 与 ^、$ 之间的位置。

  • \B:匹配一个非单词边界;即 \w 与 \w之间,\W 和 \W 之间,\W 与 ^ 、$ 之间。

  • exp1(?=exp2):查找 exp2 前面的 exp1

    '<h1>daxia</h1>'.match(/<(?=\/)/);
    // [
    //   '<', // 使用非捕获元素时,括号里的内容不会存在捕获组里
    //   index: 9,
    //   input: '<h1>daxia</h1>',
    //   groups: undefined
    // ]
    复制代码
  • (?<=exp2)exp1:查找 exp2 后面的 exp1

    '<h1>daxia</h1>'.match(/(?<=daxia)</);
    // [
    //   '<', // 使用非捕获元素时,括号里的内容不会存在捕获组里
    //   index: 9,
    //   input: '<h1>daxia</h1>',
    //   groups: undefined
    // ]
    复制代码
  • exp1(?!exp2):查找后面不是 exp2 的 exp1

  • (?<!exp2)exp1:查找前面不是 exp2 的 exp1;


8、操作符优先级

操作符描述 操作符 优先级
转义符 \ 1
括号和方括号 (…)、(?:…)、(?=…)、(?!…)、[…] 2
量词限定符 {m}、{m,n}、{m,}、?、*、+ 3
位置和序列 ^、$、\元字符、一般字符 4
管道符(竖杠) 5

注:

  • 使用连续的量词时注意要使用括号;

猜你喜欢

转载自juejin.im/post/7110140846615298062