一、基础知识

补充:

return RegExp.$1;
RegExp的静态变量,$n,表示的是最近一次使用的正则对象的匹配到的第n个符合正则对象规则要求的元素(只有 $1 ~ $9 )。找到了就将其返回,未找到返回空。

1.创建正则

有两种方式:
字面量创建:使用//包裹的字面量创建正则,但不能使用变量
(/u/.test(hd)) /true
对象创建:
使用RepExp对象构造正则对象
其语法为:https://www.w3school.com.cn/jsref/jsref_obj_regexp.asp
new RegExp(pattern, attributes);
pattern 是一个字符串,指定了正则表达式的模式或其他正则表达式
参数 attributes 是一个可选的字符串,包含属性 "g"、"i" 和 "m"等(在模式修饰部分)

其返回值是一个新的 RegExp 对象,具有指定的模式和标志。如果参数 pattern 是正则表达式而不是字符串,那么 RegExp() 构造函数将用与指定的 RegExp 相同的模式和标志创建一个新的 RegExp 对象。如果不用 new 运算符,而将 RegExp() 作为函数调用,那么它的行为与用 new 运算符调用时一样,只是当 pattern 是正则表达式时,它只返回 pattern,而不再创建一个新的 RegExp 对象。

使用例子:
let hd = "houdunren.com";
let web = "houdunren";
let reg = new RegExp(web);
console.log(reg.test(hd)); //true

2.选择符

| 这个符号带表选择修释符,类似于或逻辑
如,匹配字符是否包含houdunren 或 hdcms
const hd = "houdunren";
console.log(/houdunren|hdcms/.test(hd)); //true

3.字符转义

对于再正则中有特殊含义的符号,需要使用转义符号 \ 进行转义
使用两种方法构造正则时在转义上有所相区别:
let price = 12.23;
//含义1: . 除换行外任何字符  含义2: .普通点
console.log(/\d+\.\d+/.test(price));

//因为在字符串中 \d 与 d 是相等的,所以在 new RegExp 时\d 就是一个普通字母d \.也代表 . 
并不能起到匹配数字的作用 所以需要加上两个\\

console.log("\d" == "d");

//使用对象定义正则时,可以先把字符串打印一样,结果是字面量一样的定义就对了
console.log("\\d+\\.\\d+");
let reg = new RegExp("\\d+\\.\\d+");//需要把\d \. 前面都需要再转义一次!
console.log(reg.test(price));

4.字符边界

边界符 说明
^ 匹配字符串的开始
$ 匹配字符串的结束,忽略换行符
如:
匹配内容必须以www开始
const hd = "www.houdunren.com";
console.log(/^www/.test(hd)); //true

匹配内容必须以.com结束

const hd = "www.houdunren.com";
console.log(/\.com$/.test(hd)); //true

二、元子字符

1.字符列表

元字符 说明 示例
\d 匹配任意一个数字 [0-9]
\D 与除了数字以外的任何一个字符匹配 [^0-9]
\w 与任意一个英文字母,数字或下划线匹配 [a-zA-Z_]
\W 除了字母,数字或下划线外与任何字符匹配 [^a-zA-Z_]
\s 任意一个空白字符匹配,如空格,制表符\t,换行符\n [\n\f\r\t\v]
\S 除了空白符外任意一个字符匹配 [^\n\f\r\t\v]
. 匹配除换行符外的任意字符

正则中空格会按普通字符对待

let tel = `010 - 999999`;
console.log(/\d+-\d+/.test(tel)); //false
console.log(/\d+ - \d+/.test(tel)); //true

2.所有字符

可以使用 [\s\S] 或 [\d\D] 来匹配所有字符

三、模式修饰

修饰符 说明
i 不区分大小写字母的匹配
g 全局搜索所有匹配内容
m 视为多行
s 视为单行忽略换行符,使用. 可以匹配所有字符
y 从 regexp.lastIndex 开始匹配
u 正确处理四个字符的 UTF-16 编
m:用于将内容视为多行匹配,主要是对 ^和 $ 的修饰
u y:在ES6的总结中写
lastIndex:是RegExp对象的属性,用以可以返回或者设置正则表达式开始匹配的位置
  • 必须结合 g 修饰符使用
  • 对 exec 方法有效(在后面全局匹配中详细总结)
  • 匹配完成时,lastIndex 会被重置为0

四、原子表

原子表 说明
[] 只匹配其中的一个原子 如 [ue] 代表匹配u e中任何一个字符就可以 而不是看作整体 
[^] 只匹配"除了"其中字符的任意一个原子
[0-9] 匹配0-9任何一个数字
[a-z] 匹配小写a-z任何一个字母
[A-Z] 匹配大写A-Z任何一个字母

五、原子组

  • 原子组与原子表的差别在于原子组一次匹配多个元子,而原子表则是匹配任意一个字符
  • 元字符组用 () 包裹

1.基本使用

使用原子组匹配标题元素

let hd = `
  <h1>houdunren</h1>
  <span>后盾人</span>
  <h2>hdcms</h2>
`;

console.table(hd.match(/<(h[1-6])[\s\S]*<\/\1>/g));

2.邮箱匹配

使用原子组匹配邮箱

let hd = "2300071698@qq.com";
let reg = /^[\w\-]+@[\w\-]+\.(com|org|cn|cc|net)$/i;
console.dir(hd.match(reg));

3.引用分组

\n 在匹配时引用原子组, $n 指在替换时使用匹配的组数据。下面将标签替换为p标签。
let hd = `
  <h1>houdunren</h1>
  <span>后盾人</span>
  <h2>hdcms</h2>
`;

let reg = /<(h[1-6])>([\s\S]*)<\/\1>/gi;(这里的\1代表引用第一个原子组)
console.log(hd.replace(reg, `<p>$2</p>`));(这里的$2指使用第二个原子组的内容做替换)
第几个原子组就通过查括号来判定 第几个括号 就是第几个原子组

4.分组别名

如果希望返回的组数据更清晰,可以为原子组编号,结果将保存在返回的 groups字段中

let hd = "<h1>houdunren.com</h1>";
console.dir(hd.match(/<(?<tag>h[1-6])[\s\S]*<\/\1>/));

组别名使用 ?<> 形式定义,下面将标签替换为p标签

let hd = `
  <h1>houdunren</h1>
  <span>后盾人</span>
  <h2>hdcms</h2>
`;
let reg = /<(?<tag>h[1-6])>(?<con>[\s\S]*)<\/\1>/gi;
console.log(hd.replace(reg, `<p>$<con></p>`));

六、重复匹配

如果要重复匹配一些内容时我们要使用重复匹配修饰符,包括以下几种:

符号 说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次
如 
验证坐机号的正则
let hd = "010-12345678";
console.log(/0\d{2,3}-\d{7,8}/.exec(hd));
但在匹配重复内容时,默认是贪婪匹配模式,也就是说会尽量匹配更多内容。
可以通过?进行修饰来禁止重复匹配
使用 说明
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

禁止贪婪的语法例子

let str = "aaa";
console.log(str.match(/a+/)); //aaa
console.log(str.match(/a+?/)); //a
console.log(str.match(/a{2,3}?/)); //aa
console.log(str.match(/a{2,}?/)); //aa

七、全局匹配

当使用 match 全局获取内容时,就不会返回匹配细节了。
但以下两种方式可以获得匹配细节

1.matchAll

使用该方法,可返回迭代对象
let str = "houdunren";
let reg = /[a-z]/ig;
for (const iterator of str.matchAll(reg)) {
  console.log(iterator);
}2

2.exec

使用 g 模式修正符并结合 exec 循环操作可以获取结果和匹配细节

<body>
  <h1>houdunren.com</h1>
  <h2>hdcms.com</h2>
  <h1>后盾人</h1>
</body>
<script>
  function search(string, reg) {
    const matchs = [];
    while ((data = reg.exec( string))) {
      matchs.push(data);
    }
    return matchs;
  }
  console.log(search(document.body.innerHTML, /<(h[1-6])>[\s\S]+?<\/\1>/gi));
</script>

八、字符方法

String 中提供的支持正则表达式的方法

1.search

search() 方法用于检索字符串中指定的子字符串,也可以使用正则表达式搜索,返回值为索引位置

console.log(str.search(/\.com/i));

2.match

调用match方法进行匹配,其返回值包含:
变量 说明
0 匹配到的完整内容
1,2.... 匹配到的原子组
index 原字符串中的位置
input 原字符串
groups 命名分组

在match中使用原子组匹配,会将每个组数据返回到结果中

  • 0 为匹配到的完整内容
  • 1/2 等 为原子级内容
  • index 匹配的开始位置
  • input 原始数据
  • groups 组别名
let hd = "houdunren.com";
console.log(hd.match(/houdun(ren)\.(com)/)); 
//["houdunren.com", "ren", "com", index: 0, input: "houdunren.com", groups: undefined]
match方法默认只执行一次,只有regexp 标志 g时才能进行全局匹配
但进行全局匹配时将无法返回上述的细节信息,如
let body = document.body.innerHTML;
let result = body.match(/<(h[1-6])>[\s\S]+?<\/\1>/g);
console.table(result);

3.matchAll

matchAll进行全局匹配,并返回含有详细信息的迭代对象
let str = "houdunren";
let reg = /[a-z]/ig;
for (const iterator of str.matchAll(reg)) {
  console.log(iterator);
}

4.split

该方法可以使用字符串或正则表达式分隔字符串,如在日期的连接符不确定的情况下,使用正则:
let str = "2023/02-12";
console.log(str.split(/-|\//));

5.replace

replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串
stringObject.replace(regexp/substr,replacement)
参数 描述
regexp/substr

必需。规定子字符串或要替换的模式的 RegExp 对象。

请注意,如果该值是一个字符串,则将它作为要检索的直接量文本模式,而不是首先被转换为 RegExp 对象。

replacement 必需。一个字符串值。规定了替换文本或生成替换文本的函数。
如果 regexp 具有全局标志 g,那么 replace() 方法将替换所有匹配的子串。否则,它只替换第一个匹配子串。

替换字符串可以插入下面的特殊变量名:

变量 说明
$$ 插入一个 "$"。
$& 插入匹配的子串。
$` 插入当前匹配的子串左边的内容。
$' 插入当前匹配的子串右边的内容。
$n
假如第一个参数是 RegExp 对象,并且 n 是个小于100的非负整数
那么插入第 n 个括号匹配的字符串。提示:索引是从1开始
(即之前提到的原子组替换)


把电话号用 - 连接

let hd = "(010)99999999 (020)8888888";
console.log(hd.replace(/\((\d{3,4})\)(\d{7,8})/g, "$1-$2"))

在后盾人前后添加三个=

let hd = "=后盾人=";
console.log(hd.replace(/后盾人/g, "$`$`$&$'$'"));
并且 replace支持回调函数操作:
变量名 代表的值
match 匹配的子串。(对应于上述的$&。)
p1,p2, ...
假如replace()方法的第一个参数是一个 RegExp 对
象则代表第n个括号匹配的字符串。(对应于上述的$1,$2等。)
例如,如果是用 /(\a+)(\b+)/ 这个来匹配,
p1 就是匹配的 \a+,p2 就是匹配的 \b+。
offset
匹配到的子字符串在原字符串中的偏移量。
(比如,如果原字符串是 'abcd',匹配到的子字符串是 'bc',
那么这个参数将会是 1)
string 被匹配的原字符串。
NamedCaptureGroup 命名捕获组匹配的对象

九、正则方法

RegExp 正则对象提供的操作方法

1.test

返回值是true 或 false 用以判断是否目标字符串中是否包含该正则字段
var str = "Visit W3School";
var patt1 = new RegExp("W3School");
var result = patt1.test(str);//输出:true

2.exec

不使用 g 修饰符时与 match 方法使用相似,使用 g 修饰符后可以循环调用直到全部匹配完。

但无论是否全局,都会返回含有详细信息的数据。可以说,在循环中反复地调用 exec() 方法是唯一一种获得全局模式的完整模式匹配信息的方法。

并且,exec() 会在 RegExpObject 的 lastIndex 属性指定的字符处开始检索字符串 string。
当 exec() 找到了与表达式相匹配的文本时,在匹配后,它将把 RegExpObject 的 lastIndex 属性设置为匹配文本的最后一个字符的下一个位置。当 exec() 再也找不到匹配的文本时,它将返回 null,并把 lastIndex 属性重置为 0。

注意:如果在一个字符串中完成了一次模式匹配之后要开始检索新的字符串,就必须手动地把 lastIndex 属性重置为 0,否则将直接从下一位置开始进行匹配

十、断言匹配

断言相当于正则中的条件,虽然写在括号里,但不是原子组。

1.(?=exp)

零宽先行断言 ?=exp 匹配后面为 exp 的内容

将价格后面 添加上 .00

<script>
  let lessons = `
    js,200元,300次
    php,300.00元,100次
    node.js,180元,260次
  `;
  let reg = /(\d+)(.00)?(?=元)/gi;
  lessons = lessons.replace(reg, (v, ...args) => {
    args[1] = args[1] || ".00";
    return args.splice(0, 2).join("");
  });
  console.log(lessons);
</script>

2.(?<=exp)

零宽后行断言 ?<=exp 匹配前面为 exp 的内容

匹配前后都是数字的内容

let hd = "houdunren789hdcms666";
let reg = /(?<=\d)[a-z]+(?=\d{3})/i;
console.log(hd.match(reg));

3.(?!exp)

零宽负向先行断言 后面不能出现 exp 指定的内容  
使用 (?!exp)字母后面不能为两位数字
let hd = "houdunren12";
let reg = /[a-z]+(?!\d{2})$/i;
console.table(reg.exec(hd));

4.(?<!exp)

零宽负向后行断言 前面不能出现exp指定的内容

获取前面不是数字的字符

let hd = "hdcms99houdunren";
let reg = /(?<!\d+)[a-z]+/i;
console.log(reg.exec(hd)); //hdcms

5.(?<!exp)

零宽负向后行断言 前面不能出现exp指定的内容

获取前面不是数字的字符

let hd = "hdcms99houdunren";
let reg = /(?<!\d+)[a-z]+/i;
console.log(reg.exec(hd)); //hdcms