学会正则很简单,前端大佬悉心整理的正则表达式笔记

4,171 阅读12分钟

前言

该正则表达式笔记转自B站UP主:后盾人编程,本人只是对代码中一些较难理解的地方加了注释。

后盾人大佬视频网址为:www.bilibili.com/video/BV12J…

原博客网址为: doc.houdunren.com/

1.体验正则表达式的魅力

案例: 过滤出字符串中的数字

1.普通函数操作正则表达式

    let hd = "houdunren2200hdcms9988";

    let nums = [...hd].filter(a => !Number.isNaN(parseInt(a)))

    console.log(nums.join(""));     //22009988

2.正则表达式匹配字符串

    console.log(hd.match(/\d/g).join(""));      //22009988

可以看出使用正则表达式大大简化了代码的编写。

2.创建正则

JS使用字面量与对象两种方式来创建正则表达式

使用字面量创建正则表达式

使用 //包裹的字面量创建方式是推荐的方式,但不能在其中使用变量

    let hd = 'houdunren.com'
    console.log(/u/.test(hd));  //true

当a为变量时将不可以查询

    let a = 'u';
    console.log(/a/.test(hd));  //false

虽然可以使用 eval 转换为js语法来实现将变量解析到正则中,但是比较麻烦,所以有变量时建议使用下面的对象创建方式

eval()函数详解:如果参数是一个表达式,eval() 函数将执行表达式。如果参数是Javascript语句,eval()将执行 Javascript 语句。

    console.log(eval(`/${a}/`))  //  /u/
    console.log(eval(`/${a}/`).test(hd));  //true

使用对象创建正则表达式

当正则需要动态创建时使用对象方式

test方法返回的是布尔值true和false

match方法返回的是匹配到的结果数组

    let hd = "houdunren.com";
    //创建一个RegExp对象
    let reg = new RegExp("u","g");
    console.log(reg);     // /u/g
    console.log(reg.test(hd));  // true
    console.log(hd.match(reg))  // ['u', 'u']

根据用户输入高亮显示内容,支持用户输入正则表达式

replace() 第一个参数是子字符串或要替换的模式的 RegExp 对象,

第二个参数是一个字符串值,规定了替换文本或生成替换文本的函数。(如果第二个参数是函数,则匹配成功后就是利用该函数对第一个参数进行操作返回后替换第一个参数的值)

    const con = prompt("请输入要搜索的内容,支持正则表达式");
    const reg = new RegExp(con,"g");
    let div = document.querySelector('div');
    div.innerHTML = div.innerHTML.replace(reg,search => {
        console.log(search)
        return `<span style="color:red">${search}</span>`;
    })

3.选择符

|代表选择修饰符,也就是|左右两侧有一个匹配到的就可以

检测电话是否是上海或北京的坐机

    let tel = "010-12345677";
    //错误结果: 只匹配 | 左右两侧任一结果,tel是“010”也能匹配成功
    console.log(tel.match(/010|020\-\d{7,8}/))
    //正确结果:所有需要放在原子组中使用
    console.log(tel.match(/(010|020)\-\d{7,8}/))

匹配字符是否包含houdunrenhdcms

    const hd = "houdunren";
    console.log(/houdunren|hdcms/.test(hd));  //true

4.转义

转义用于改变字符的含义,用来对某个字符有多种语义时的处理。

假如有这样的场景,使用字面量创建正则表达式查找/符号,但是/在正则中有特殊的含义,如果写成///会造成解析错误,所以要使用转义语法/\//来匹配。

匹配url中/

    const url = "https://www.houdunren.com";
    console.log(/https:\/\//.test(url));

使用 RegExp 构建正则时在转义上会有些区别,下面是对象与字面量定义正则时区别

    let price = 12.23;
//①字面量    
    //"."的含义1: . 除换行外任何字符  含义2: . 普通点
    //含义1: d 字母d            含义2: \d 数字0-9
    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+");
    console.log(reg.test(price));

5.字符边界

边界符说明
^匹配字符串的开始
$匹配字符串的结束,忽略换行符

匹配内容以www开始

    const hd = "www.houdunren.com";
    console.log(/^www/.test(hd));

匹配内容以.com结束

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

检测用户名长度为3~6位,且只能为字母。如果不使用 ^与$ 限制将得不到正确结果

<body>
    <input type="text" name="username">
</body>
<script>
    document.querySelector(`[name="username"]`)
            .addEventListener("keyup",function() {
                //不适用^,字面量中第一个字符不一定是字母,
                //不使用$,则结束时不一定是3-6个字母,也不一定最后一位是字母
                let res = this.value.match(/^[a-z]{3,6}$/i);
                console.log(res);
                console.log(res ? "正确":"错误");
            })
</script>
</html>

6.元子字符

元字符是正则表达式中最小元素,只代表单一(一个)元素

字符列表

元字符说明示例
\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 hd = "houdunren 2010";
    console.log(hd.match(/\d/g));//['2', '0', '1', '0']

匹配所有电话号码

    let hd = `
	张三:010-99999999,李四:020-88888888
    `;
    let res = hd.match(/\d{3}-\d{7,8}/g);
    console.log(res);

获取所有用户名

    let hd = `
    张三:010-99999999,李四:020-88888888`;
    let res = hd.match(/[^:\d\-,]+/g);
    console.log(res);

匹配任意非数字

console.log(/\D/.test(2029)); //false

匹配字母数字下划线

let hd = "hdcms@";
console.log(hd.match(/\w/g)); //["h", "d", "c", "m", "s"]

匹配除了字母,数字或下划线外与任何字符匹配

console.log(/\W/.test("@")); //true

匹配与任意一个空白字符匹配

console.log(/\s/.test(" ")); //true
console.log(/\s/.test("\n")); //true

匹配除了空白符外任意一个字符匹配

let hd = "hdcms@";
console.log(hd.match(/\S/g)); ////['h', 'd', 'c', 'm', 's', '@']

如果要匹配点则需要转义

let hd = `houdunren@com`;
console.log(/houdunren.com/i.test(hd)); //true
console.log(/houdunren\.com/i.test(hd)); //false

使用.匹配除换行符外任意字符,下面匹配不到hdcms.com 因为有换行符

const url = `
  https://www.houdunren.com
  hdcms.com
`;
console.log(url.match(/.+/)[0]);

使用/s视为单行模式(忽略换行)时,. 可以匹配所有

let hd = `
  <span>
    houdunren
    hdcms
  </span>
`;
let res = hd.match(/<span>.*<\/span>/s);
console.log(res[0]);

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

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

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

let hd = `
  <span>
    houdunren
    hdcms
  </span>
`;
let res = hd.match(/<span>[\s\S]+<\/span>/);
console.log(res[0]);

7.模式修饰

修饰符说明
i不区分大小写字母的匹配
g全局搜索所有匹配内容
m视为多行匹配
s视为单行忽略换行符,使用. 可以匹配所有字符
yregexp.lastIndex 开始匹配
u正确处理四个字符的 UTF-16 编码

i

将所有houdunren.com 统一为小写

    let hd = "houdunren.com HOUDUNREN.COM"

    hd = hd.replace(/houdunren\.com/gi,"houdunren.com");

    console.log(hd);

g

使用 g 修饰符可以全局操作内容

let hd = "houdunren";
hd = hd.replace(/u/, "@");
console.log(hd); //没有使用 g 修饰符时,只替换了第一个

let hd = "houdunren";
hd = hd.replace(/u/g, "@");
console.log(hd); //使用全局修饰符后替换了全部的 u

m

用于将内容视为多行匹配,主要是对 ^$ 的修饰

下面是将以 #数字开始的课程解析为对象结构,学习过后面讲到的原子组可以让代码简单些

    let hd = `
        #1 js,200元 #
        #2 php,300元 #
        #9 houdunren.com # 后盾人
        #3 node.js,180元 #
    `;
    // 变为[{name:'js',price:'200元'}]形式
    let lessons = hd.match(/^\s*#\d+\s*.+\s+#$/gm).map(v => {
        v = v.replace(/\s*#\d+\s*/,"").replace(/\s+#/,"");
        [name,price] = v.split(",");
        //{}是以键值对方式返回
        return { name,price }; 
    })
   console.log(JSON.stringify(lessons,null,2))
   /*运行结果为:
    [
        {
            "name": "js",
            "price": "200元"
        },
        {
            "name": "php",
            "price": "300元"
        },
        {
            "name": "node.js",
            "price": "180元"
        }
    ]
   */

u

每个字符都有属性,如L属性表示是字母,P 表示标点符号,需要结合 u 模式才有效。其他属性简写可以访问 属性的别名 (opens new window) 网站查看。

//使用\p{L}属性匹配字母
let hd = "houdunren2010.不断发布教程,加油!";
console.log(hd.match(/\p{L}+/u));

//使用\p{P}属性匹配标点
console.log(hd.match(/\p{P}+/gu));

字符也有unicode文字系统属性 Script=文字系统,下面是使用 \p{sc=Han} 获取中文字符 han为中文系统,其他语言请查看 文字语言表

let hd = `
张三:010-99999999,李四:020-88888888`;
let res = hd.match(/\p{sc=Han}+/gu);
console.log(res);

使用 u 模式可以正确处理四个字符的 UTF-16 字节编码

let str = "𝒳𝒴";
console.table(str.match(/[𝒳𝒴]/)); //结果为乱字符"�"

console.table(str.match(/[𝒳𝒴]/u)); //结果正确 "𝒳"

lastIndex

RegExp对象lastIndex 属性可以返回或者设置正则表达式开始匹配的位置

-- 必须结合 g 修饰符使用

-- 对 exec 方法有效

-- 匹配完成时,lastIndex 会被重置为0

    let hd = `后盾人不断分享视频教程,后盾人网址是 houdunren.com`;
    let reg = /后盾人(.{2})/g;
    console.log(reg.exec(hd));  // '后盾人不断'
    reg.lastIndex = 11; //从索引10开始往后搜索
    console.log(reg.exec(hd)); //'后盾人网址' (注意只对exec方法有效)
    let hd = `后盾人不断分享视频教程,后盾人网址是 houdunren.com`;
    reg = /\p{sc=Han}/gu;
    //exec会记录上次搜索的lastIndex,所以会遍历出所有中文
    //一定要配合g使用
    while((res = reg.exec(hd))) {
        console.log(res[0]);
    }

y

我们来对比使用 yg 模式,使用 g 模式会一直匹配字符串

let hd = "udunren";
let reg = /u/g;
console.log(reg.exec(hd));
console.log(reg.lastIndex); //1
console.log(reg.exec(hd));
console.log(reg.lastIndex); //3
console.log(reg.exec(hd)); //null
console.log(reg.lastIndex); //0

但使用y 模式后如果从 lastIndex 开始匹配不成功就不继续匹配了

let hd = "udunren";
let reg = /u/y;
console.log(reg.exec(hd));
console.log(reg.lastIndex); //1
console.log(reg.exec(hd)); //null
console.log(reg.lastIndex); //0  //lastIndex重新回到0

因为使用 y 模式可以在匹配不到时停止匹配,在匹配下面字符中的qq时可以提高匹配效率

let hd = `后盾人QQ群:11111111,999999999,88888888
后盾人不断分享视频教程,后盾人网址是 houdunren.com`;

let reg = /(\d+),?/y;
reg.lastIndex = 7;
while ((res = reg.exec(hd))) console.log(res[1]);

8.原子表

在一组字符中匹配某个元字符,在正则表达式中通过元字符表来完成,就是放到[] (方括号)中。

使用语法

原子表说明
[]只匹配其中的一个原子
[^]只匹配"除了"其中字符的任意一个原子
[0-9]匹配0-9任何一个数字
[a-z]匹配小写a-z任何一个字母
[A-Z]匹配大写A-Z任何一个字母

实例操作

使用[]匹配其中任意字符即成功,下例中匹配ue任何一个字符,而不会当成一个整体来对待

const url = "houdunren.com";
console.log(/ue/.test(url)); //false
console.log(/[ue]/.test(url)); //true

日期的匹配

let tel = "2022-02-23";
//这里() 和 \1 是原子组的知识,后面啊会细讲
console.log(tel.match(/\d{4}([-/])\d{2}\1\d{2}/));

获取0~3间的任意数字

const num = "2";
console.log(/[0-3]/.test(num)); //true

匹配a~f间的任意字符

const hd = "e";
console.log(/[a-f]/.test(hd)); //true

顺序为升序否则将报错

const num = "2";
console.log(/[3-0]/.test(num)); //SyntaxError

字母也要升序否则也报错

const hd = "houdunren.com";
console.log(/[f-a]/.test(hd)); //SyntaxError

获取所有用户名

“-”在[]中是区间匹配的意思,所以要转义

    let hd = `
    张三:010-99999999,李四:020-88888888`;
    //“-”在[]中是区间匹配的意思,所以要转义
    let res = hd.match(/[^:\d\-,]+/g);
    console.log(res);

原子表中有些正则字符不需要转义,如果转义也是没问题的,可以理解为在原子表中. 就是小数点

let str = "(houdunren.com)+";
console.table(str.match(/[().+]/g));

//使用转义也没有问题
console.table(str.match(/[\(\)\.\+]/g));

可以使用 [\s\S][\d\D]匹配到所有字符包括换行符

...
const reg = /[\s\S]+/g;
...

下面是使用原子表知识删除所有标题

<body>
  <p>后盾人</p>
  <h1>houdunren.com</h1>
  <h2>hdcms.com</h2>
</body>
<script>
  const body = document.body;
  const reg = /<(h[1-6])>[\s\S]*</\1>*/g;
  let content = body.innerHTML.replace(reg, "");
  document.body.innerHTML = content;
</script>

9.原子组

-- 如果一次要匹配多个元子,可以通过元子组完成

-- 原子组与原子表的差别在于原子组一次匹配多个元子,而原子表则是匹配任意一个字符

-- 元字符组用 () 包裹

基本使用

没有添加 g 模式修正符时只匹配到第一个,匹配到的信息包含以下数据

变量说明
0匹配到的完整内容
1,2....匹配到的原子组
index原字符串中的位置
input原字符串
groups命名分组

下面使用原子组匹配 h 标签得到的结果,

     let hd = `<h1>houdunren</h1>
     <h2>hdcms</h2>
    `
    let reg = /<(h[1-6])>([\s\S]*)<\/\1>/i;
    console.log(hd.match(reg));

图片.png

邮箱匹配

下面使用原子组匹配邮箱(邮箱可以包含“-”)

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

如果邮箱是以下格式 houdunren@hd.com.cn 上面规则将无效,需要定义以下方式

let hd = `admin@houdunren.com.cn`;
let reg = /^[\w\-]+@([\w\-]+\.)+(org|com|cc|cn)$/;
console.log(hd.match(reg));

引用分组

\n (n为1,2,3...)在匹配时引用原子组, $n (n为1,2,3...)指在替换时使用匹配的组数据。下面将标签替换为p标签

let hd = `
  <h1>houdunren</h1>
  <span>后盾人</span>
  <h2>hdcms</h2>
`;
//替换方式一,推荐
    let reg = /<(h[1-6])>([\s\S]*)<\/\1>/gi;
    console.log(hd.replace(reg,`<p>$2</p>`))   //$2为第二个括号()匹配到的内容
//替换方式二
    let res = hd.replace(reg,(p0,p1,p2,p3) => {
        console.log('p0:',p0);  //p0: <h1>houdunren</h1>  (匹配到全内容)
        console.log('p1:',p1);  //p1: h1  (第一个())
        console.log('p2:',p2);  //p2: houdunren (第二个())
        console.log('p3:',p3);  //p3: 3 (索引开始位置)
        return `<p>${p2}</p>`;
    });
    console.log(res);

路由匹配

    let hd = `
        https://www.houdunren.com
        http://houdunwang.com
        https://hdcms.com
    `;

匹配含二级域名的整条url

let reg = /https:\/\/\w+\.\w+\.(com|org|cn)/gi;

取域名记录,加()

let reg = /https:\/\/(\w+\.\w+\.(com|org|cn))/gi;

不要\2或者$2的记录  在(后加?:

let reg = /https:\/\/(\w+\.\w+\.(?:com|org|cn))/gi;

https或http 都可匹配,在s后加?

let reg = /https?:\/\/(\w+\.\w+\.(?:com|org|cn))/gi;

www有可能有,也有可能没有, 用()括起来加后加?,加?: 不记录www

let reg = /https?:\/\/((?:\w+\.)?\w+\.(?:com|org|cn))/gi;

10.重复匹配

基本使用

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

符号说明
*重复零次或更多次
+重复一次或更多次
?重复零次或一次
{n}重复n次
{n,}重复n次或更多次
{n,m}重复n到m次

因为正则最小单位是元字符,而我们很少只匹配一个元字符如a、b所以基本上重复匹配在每条正则语句中都是必用到的内容。 默认情况下重复选项对单个字符进行重复匹配,即不是贪婪匹配

let hd = "hdddd";
console.log(hd.match(/hd+/i)); //hddd

使用原子组后则对整个组重复匹配

let hd = "hdddd";
console.log(hd.match(/(hd)+/i)); //hd

下面是验证坐机号的正则

let hd = "010-12345678";
console.log(/0\d{2,3}-\d{7,8}/.exec(hd));

验证用户名只能为3~8位的字母或数字,并以字母开始

<body>
    <input type="text" name="username">
</body>
<script>
    let input = document.querySelector(`[name="username"]`);
    input.addEventListener("keyup",e => { 
        const value = e.target.value;
        let state = /^[a-z][\w]{2,7}$/i.test(value);
        console.log(
            state ? "正确!" : "用户名只能为3-8位的字母或数字,并以字母开始"
        );
    })
</script>

验证密码必须包含大写字母并在5~10位之间

    let input = document.querySelector(`[name="password"]`);
    input.addEventListener("keyup",e => { 
        const value = e.target.value.trim();
        const regs = [/^[a-zA-Z0-9]{5,10}$/,/[A-Z]/];
        //利用every测试每一项都满足才返回真,否则返回假
        let state = regs.every(v => v.test(value));
        console.log(state ? "正确" : "密码必须包含大写字母并在5-10位之间")
    });

禁止贪婪

正则表达式在进行重复匹配时,默认是贪婪匹配模式,也就是说会尽量匹配更多内容,但是有的时候我们并不希望他匹配更多内容,这时可以通过?进行修饰来禁止重复匹配

使用说明
*?重复任意次,但尽可能少重复
+?重复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

将所有span更换为h4 并描红,并在内容前加上 后盾人-

<body>
  <main>
    <span>houdunwang</span>
    <span>hdcms.com</span>
    <span>houdunren.com</span>
  </main>
</body>
<script>
  const main = document.querySelector("main");
  //匹配以<span>开头,以</span>结尾的尽可能少的内容
  /*
      这里[\s\S]会匹配所有内容,所以遇到第一个</span>也不会当做</span>处理,
      遇到最后一个</span>才会当做</span>处理,但是加了+?后就不会有这种情况
 */
  const reg = /<span>([\s\S]+?)<\/span>/gi;
  main.innerHTML = main.innerHTML.replace(reg, (v, p1) => {
    console.log(p1);
    return `<h4 style="color:red">后盾人-${p1}</h4>`;
  });
</script>

下面是使用禁止贪婪查找页面中的标题元素

<body>
    <h1>
      houdunren.com
    </h1>
    <h2>hdcms.com</h2>
    <h3></H3>
    <H1></H1>
</body>
<script>
    let body = document.body.innerHTML;
    //这里如果不使用禁止贪婪,因为忽视大小写,<h1>会对应到</H1>,只能得到一个标题元素
    let reg = /<(h[1-6])>[\s\S]*?<\/\1>/gi;
    console.table(body.match(reg));
</script>

11.字符方法

search

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

let str = "houdunren.com";
console.log(str.search(".com"));

使用正则表达式搜索

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

match

直接使用字符串搜索,返回值为一组详细信息

    let str = "houdunren.com";
    console.dir(str.match(".com"));

图片.png

使用正则获取内容,返回值也为一组详细信息,下面是简单的搜索字符串

    let hd = "houdunren";
    let res = hd.match(/u/);
    console.log(res);
    console.log(res[0]); //匹配的结果

图片.png 如果使用 g 修饰符时,就不会有结果的详细信息了(可以使用exec),下面是获取所有h1~6的标题元素

<body>
    <p>后盾人</p>
    <h1>houdunren.com</h1>
    <h2>hdcms.com</h2>
</body>
<script>
    let body = document.body.innerHTML;
    let result = body.match(/<(h[1-6])>[\s\S]+?<\/\1>/g);
    console.dir(result);
</script>

图片.png

上面只是匹配的结果,没由详细信息

matchAll

在新浏览器中支持使用 matchAll 操作,并返回迭代对象

    let str = "houdunren";
    let reg = /[a-z]/ig;
    //直接使用matchAll返回一个迭代对象
    let res = str.matchAll(reg);
    console.log(res);

图片.png

利用for of 遍历迭代对象

    let str = "houdunren";
    let reg = /[a-z]/ig;
    //遍历迭代对象
    for (const iterator of str.matchAll(reg)) {
        console.log(iterator);
    }

图片.png

split

用于使用字符串或正则表达式分隔字符串,下面是使用字符串分隔日期

    let str = "2023-02-12";
    console.log(str.split("-")); //["2023", "02", "12"]

如果日期的连接符不确定,那就要使用正则操作了

    let str = "2023/02-12";
    console.log(str.split(/-|\//));// ['2023', '02', '12']

replace

replace 方法不仅可以执行基本字符替换,也可以进行正则替换,下面替换日期连接符

    let str = "2023/02/12";
    console.log(str.replace(/\//g, "-")); //2023-02-12

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

变量说明
$$插入一个 "$"。
$&插入匹配的子串。
$`插入当前匹配的子串左边的内容。
$'插入当前匹配的子串右边的内容。
$n假如第一个参数是 RegExp 对象,插入第 n 个括号匹配的字符串(0<n<100),提示:索引是从1开始

在后盾人前后添加三个=

    let hd = "=后盾人=";
    console.log(hd.replace(/后盾人/g, "$`$`$&$'$'"));//===后盾人===

把电话号用 - 连接

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

把所有教育汉字加上链接 https://www.houdunren.com

<body>
    在线教育是一种高效的学习方式,教育是一生的事业
</body>
<script>
    const body = document.body;
    body.innerHTML = body.innerHTML.replace(
        /教育/g,
        `<a href="https://www.houdunren.com">$&</a>`
    );
</script>

为链接添加上https ,并补全 www.

<body>
    <main>
      <a style="color:red" href="http://www.hdcms.com">
        开源系统
      </a>
      <a id="l1" href="http://houdunren.com">后盾人</a>
      <a href="http://yahoo.com">雅虎</a>
      <h4>http://www.hdcms.com</h4>
    </main>
</body>
<script>
    const main = document.querySelector("body main");
    const reg = /(<a.*href=['"])(http)(:\/\/)(www\.)?(hdcms|houdunren)/gi;
    main.innerHTML = main.innerHTML.replace(reg, (v, ...args) => {
        //v对应a.*href=['"],args[1]对应http
        args[1] += "s";
        //如果args[3]是undefined,就添加www.,不是就保持原样就可以
        args[3] = args[3] || "www.";
        console.log(args)
        //利用splice删除前五个元素,返回值为被删除元素,接收返回值后连接起来
        return args.splice(0, 5).join("");
    });
</script>

将标题标签全部替换为 p 标签

<body>
    <h1>houdunren.com</h1>
    <h2>hdcms.com</h2>
    <h1>后盾人</h1>
</body>
<script>
    const reg = /<(h[1-6])>(.*?)<\/\1>/g;
    const body = document.body.innerHTML;
    const html = body.replace(reg, function(str, tag, content) {
        return `<p>${content}</p>`;
    });
    document.body.innerHTML = html;
</script>

删除页面中的 h1~h6 标签

<body>
    <h1>houdunren.com</h1>
    <h2>hdcms.com</h2>
    <h1>后盾人</h1>
</body>
<script>
    const reg = /<(h[1-6])>(.*?)<\/\1>/g;
    const body = document.body.innerHTML;
    const html = body.replace(reg, "");
    document.body.innerHTML = html;
</script>

原子组别名

使用?<>给原子组起别名

使用别名替换h标签为p标签

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

利用别名获取连接内容

<body>
    <main>
          <a href="https://www.hdcms.com">开源系统</a>
          <a id="l1" href="https://www.houdunren.com">后盾人</a>
          <a href="https://www.yahoo.com">雅虎</a>
    </main>
</body>
<script>
   const main = document.querySelector("body main");
   const reg = /<a.*?href=(['"])(?<link>.*?)\1>(?<title>.*?)<\/a>/ig;
   const links = [];
   for(const iterator of main.innerHTML.matchAll(reg)) { 
    //groups是别名内容   
    links.push(iterator["groups"]);
   }
   console.log(links);
</script>

图片.png

图片.png

12.正则方法

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

test

检测输入的邮箱是否合法

<body>
    <input type="text" name="email" />
</body>
<script>
    let email = document.querySelector(`[name="email"]`);
    email.addEventListener("keyup", e => {
        console.log(/^\w+@\w+\.\w+$/.test(e.target.value));
    });
</script>

exec

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

-- 使用 g 修饰符多次操作时使用同一个正则,即把正则定义为变量使用

-- 使用 g 修饰符最后匹配不到时返回 null

计算内容中后盾人出现的次数

<body>
    <div class="content">
      后盾人不断分享视频教程,后盾人网址是 houdunren.com
    </div>
</body>
<script>
    let content = document.querySelector(".content");
    let reg = /(?<tag>后盾)人/g;
    let num = 0;
    while ((result = reg.exec(content.innerHTML))) {
        num++;
    }
    console.log(`后盾人共出现${num}次`);
</script>

13.全局匹配

问题分析

下面是使用match 全局获取页面中标签内容,但并不会返回匹配细节

<body>
  <h1>houdunren.com</h1>
  <h2>hdcms.com</h2>
  <h1>后盾人</h1>
</body>

<script>
  function elem(tag) {
    const reg = new RegExp("<(" + tag + ")>.+?<\/\\1>", "g");
    return document.body.innerHTML.match(reg);
  }
  console.table(elem("h1"));
</script>

matchAll

在新浏览器中支持使用 matchAll 操作,并返回迭代对象

需要添加 g 修饰符

let str = "houdunren";
let reg = /[a-z]/ig;
for (const iterator of str.matchAll(reg)) {
  console.log(iterator);
}

在新型浏览器中工作时,可以使用matchAll匹配内容

<body>
    <h1>houdunren.com</h1>
    <h2>hdcms.com</h2>
    <h1>后盾人</h1>
</body>
<script>
    let reg = /<(h[1-6])>([\s\S]+?)<\/\1>/gi;
    const body = document.body;
    const hd = body.innerHTML.matchAll(reg);
    let contents = [];
    for(const iterator of hd) { 
        contents.push(iterator[2]);
    }
    console.table(contents);
</script>

在原型定义 matchAll方法,用于在旧浏览器中工作,不需要添加g 模式运行

String.prototype.matchAll = function(reg) {
  let res = this.match(reg);
  if (res) {
    let str = this.replace(res[0], "^".repeat(res[0].length));
    let match = str.matchAll(reg) || [];
    return [res, ...match];
  }
};
let str = "houdunren";
console.dir(str.matchAll(/(U)/i));

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>

使用上面定义的函数来检索字符串中的网址

let hd = `https://hdcms.com  
https://www.sina.com.cn
https://www.houdunren.com`;

let res = search(hd, /https?:\/\/(\w+\.)?(\w+\.)+(com|cn)/gi);
console.dir(res);

14.断言匹配

断言虽然写在扩号中但它不是组,所以不会在匹配结果中保存,可以将断言理解为正则中的条件。

(?=exp)

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

把后面是教程 的后盾人汉字加上链接

<body>
    <main>
        后盾人不断分享视频教程,学习后盾人教程提升编程能力。
    </main>
</body>
<script>
    const main = document.querySelector("main");
    //断言是正则中的条件
    const reg = /后盾人(?=教程)/gi;
    main.innerHTML = main.innerHTML.replace(
        reg,
        v => `<a href="https://houdunren.com">${v}</a>`
    );
</script>

图片.png

下面是将价格后面 添加上 .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]代表(.00),没有.00的是undefined,重新赋值上.00
      args[1] = args[1] || ".00";
      return args.splice(0, 2).join("");
    });
    console.log(lessons);
</script>

使用断言验证用户名必须为五位字母,下面正则体现断言是不是组,并且不在匹配结果中记录

<body>
    <input type="text" name="username" />
</body>
<script>
    document
        .querySelector(`[name="username"]`)
        .addEventListener("keyup", function() {
        let reg = /^(?=[a-z]{5}$)/i;
        console.log(reg.test(this.value));
        });
</script>

(?<=exp)

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

匹配前面是houdunren 的数字

let hd = "houdunren789hdcms666";
let reg = /(?<=houdunren)\d+/i;
console.log(hd.match(reg)); //789

匹配前后都是数字的内容

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

所有超链接替换为houdunren.com

    const body = document.body;
    //匹配前面是a href内容,第一个括号代表是的条件,所以\1代表的是['"]
    let reg = /(?<=<a.*href=(['"])).+?(?=\1)/gi;
    console.log(body.innerHTML.match(reg));
    body.innerHTML = body.innerHTML.replace(reg, "https://houdunren.com");

下例中将 后盾人 后面的视频添加上链接

<body>
    <h1>后盾人视频不断录制案例丰富的视频教程</h1>
</body>
<script>
    let h1 = document.querySelector("h1");
    let reg = /(?<=后盾人)视频/;
    h1.innerHTML = h1.innerHTML.replace(reg, str => {
        return `<a href="https://www.houdunren.com">${str}</a>`;
    });
</script>

将电话的后四位模糊处理

    let users = `
    向军电话: 12345678901
    后盾人电话: 98745675603
    `;
    let reg = /(?<=\d{7})\d+\s*/g;
    users = users.replace(reg, str => {
        return "*".repeat(4);
    });
    console.log(users); //向军电话: 1234567****后盾人电话: 9874567****

获取标题中的内容

    let hd = `<h1>后盾人视频不断录制案例丰富的视频教程</h1>`;
    let reg = /(?<=<h1>).*(?=<\/h1>)/g;
    console.log(hd.match(reg));

(?!exp)

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

使用 (?!exp)字母后面不能为两位数字

    let hd = "houdunren12";
    let reg = /[a-z]+(?!\d{2})$/i;
    console.table(reg.exec(hd));//null

下例为用户名中不能出现向军

<body>
    <main>
      <input type="text" name="username" />
    </main>
</body>
<script>
    const input = document.querySelector(`[name="username"]`);
    input.addEventListener("keyup", function() {
        //不可以出现向军二字
        const reg = /^(?!.*向军.*)[a-z]{5,6}$/i;
        console.log(this.value.match(reg));
    });
</script>

(?<!exp)

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

获取前面不是数字的字符

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

把所有不是以 https://oss.houdunren.com 开始的静态资源替换为新网址

<body>
    <main>
      <a href="https://www.houdunren.com/1.jpg">1.jpg</a>
      <a href="https://oss.houdunren.com/2.jpg">2.jpg</a>
      <a href="https://cdn.houdunren.com/2.jpg">3.jpg</a>
      <a href="https://houdunren.com/2.jpg">3.jpg</a>
    </main>
</body>
<script>
    const main = document.querySelector("main");
    //找到.houdunren前面不是oss的
    const reg = /https:\/\/(\w+)?(?<!oss)\..+?(?=\/)/gi;
    main.innerHTML = main.innerHTML.replace(reg, v => {
        console.log(v);
        return "https://oss.houdunren.com";
    });
</script>