Administrator
发布于 2022-03-29 / 28 阅读 / 0 评论 / 0 点赞

正则表达式

正则表达式

正则表达式并不是JavaScript里面独有的技术,基本上所有的编程语言都支持这个技术。

正则表达式有以下几个特点【它的特点也可以认为是它的使用场景】

  1. 正则表达式只对字符串进行操作
  2. 正则表达式做的是根据你所设定的规则对字符串进行“验证”,“提取”,“搜索”,“替换”等操作
  3. JavaScript中的正则表达式是一个内置对象,它可以直接使用,它使用RegExp构造函数来创建,或直接使用字面量表示法 /正则规则/

正则表达式分为三个部分

TypeError: Cannot read properties of undefined (reading 'v')

正则表达式的创建

正则表达式它是一个对象,我们要使用对象的方法去创建它的,它的构造函数是RegExp,它的语法如下

第一种创建方式

var reg = new RegExp(pattern: string | RegExp, flags?: string): RegExp

上面的pattern代表的是规则,后面的flags代表的是修饰符

所以根所据上面的语法要求,我们可以偿试着去创建一个正则

var reg1 = new RegExp("爱");   //这就是最基本的正则表达式创建
var reg2 = new RegExp("","g");	//这里就添加了一个修饰符

第二种创建方式

这一种创建方式我们叫字面量创建,它直接使用/规则/这种方式来创建

var reg2 = /爱/;
console.log(reg2);
console.log(typeof reg2);

上面就是正则表达式的创建过程,在学习正则表达式的过程当中,我们先了解正则表达式的两个方法

  1. test()方法,用于验证某个字符串是否符合这个正则表达式规则
  2. exec()方法,用于根据正则表达式去字符串中提取符合要求的字符

根据上面的两个方法,我们才可以学习正则表达式

同时要注意,正则表达式在创建的过程当中,它还可以添加修饰符flags,主要的修饰符有3个

  1. g代表全局global的意思
  2. i代表ignore忽略大小写
  3. m代表multipleline多行的意思,也就是可以换行查找

正则表达式的规则

一元符

元字符 对应说明
. 匹配除换行符之外的任意字符
\w 匹配字母数字下划线,等同于:[a-zA-Z0-9_]
\s 匹配任意空白符
\d 匹配数字,等同于[0-9]
\b 匹配单词边界
| 或匹配,如 /x|y/ 正则可匹配x或y两个字符
^ 匹配字符串的开始
$ 匹配字符串的结束

原子表与原子组

原子表

 //现在我希望匹配以下的东西杨标,陈标,张标
// 原子表指的是[]
// 原子组指的是()
var reg = /^[杨陈张]标$/;
reg.test("杨标");		//true
reg.test("陈标");		//true
reg.test("张标");		//true
reg.test("李标");     //false

原子表是以中括号的形式存在,它会从这个表中拿一个出来进行匹配

在原子表里面,还可以写区间范围

var reg1 = /[a-z]/;  //代表小写字母
var reg2 = /[A-Z]/;  //代表大写字母
var reg3 = /[0-9]/;  //代表数字,相当于\d

注意:原子表中的范围不能倒着写,写反了会报错

原子组

//现在我希望匹配以下的东西杨标,陈标,张标
var reg = /杨标|陈标|张标/;
var reg1 = /[杨陈张]标/;
//上面的正则表达式是符合要求的,但是很繁琐
var reg2 = /(杨|陈|张)标/;
//如果是用小括号包起来,就是原子组

原子组后期的优点非常多

var str = "大家好,今天是2021-01-06 14:48:30,今天天气不好,有点冷";

var reg = /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/;

var result = reg.exec(str);   //通过正则表达式提取的结果

上面的案例就是通过原子组来进行的二次提取操作

反义字符

反义字符 对应说明
[^x] 匹配除“x”之外的所有字符,其中“x”可以为任意字符
[^xyz] 同上,匹配除“x、y、z”之外的任意字符
\W 匹配除了字母、数字、下划线之外的所有字符,等同于:[^\w]
\S 匹配除空白符之外的任意字符,等同于:[^\s]
\B 匹配不是单词边界的字符,等同于:[^\b]
\D 匹配不是数字的所有字符,等同于:[^\d]

转义字符

转义字符 对应说明
\xnn 匹配十六进制数
\f 匹配换页符,等同于:\x0c
\n 匹配换行符,等同于:\x0a
\r 匹配回车符,等同于:\x0d
\t 匹配水平制表符,等同于:\x09
\v 匹配垂直制表符,等同于:\x0b
\unnnn 匹配Unicode字符,如:\u00A0

正则表达式里面一些特殊的东西是需要转义的,如[,],/,.{,},+,*,?等,转义需要使用\表示

重复匹配

匹配字符 对应说明
* 重复出现零次或多次
+ 重复出现一次或多次
重复出现零次或一次
重复出现n次
至少重复出现n次
重复重现m到n次,其中,m<n
//我们希望验证一个字符串,它是a开始,以c结束。中间有4个英文字母怎么办呢
var reg = /^a[a-zA-Z]c$/;
//第一种写法,直接写4次,这是笨办法
var reg1 = /^a[a-zA-Z]{4}c$/;

//验证一个字符串,它是a开始,c结束,中间有4~6个数字,怎么办
var reg2 = /^a\d{4,6}c$/;

//验证一个字符串,它是a开始,c结束,中间有至少4个数字,怎么办
var reg3 = /^a\d{4,}c$/;

上面的写法是我们的m-n的写法,还有一些特殊的写法

//验证一个字符串,以a开始,c结束,中间可以有1个或多个数字
var reg4= /^a\d{1,}c$/;
//或
var reg5 = /^a\d+c$/;   //+号代表1次多或次

//验证一个字符串,以a开始,以c结束,中间有0次或多次出数字
var reg6= /^a\d*c$/

//有一个姓名,它以杨开始,以标结束,中间可以有1个或0个字符,怎么办呢
var reg =  /^杨.?标$/

贪婪与惰性

相关字符 对应说明
*? 重复任意次,但尽可能少的重复
+? 重复一次或多次,但尽可能少的重复
?? 重复零次或一次,但尽可能少的重复
{m,n}? 重复m到n次,但尽可能少的重复
{n,}? 重复n次以上,但尽可能少的重复
var str = "cbcertydiouycesdfsertd";

//我的要求,提取以c开始,d结束,中间是任何长度的英文字母
var reg = /c[a-z]*d/;
// 这个时候的正则是有问题的,*处理贪婪模式,它会贪式,(*代表0次到多次,它会贪多次)
//如果我们直接以reg为标准去匹配,我们会发现,它会从头匹配到尾,但是字符串中间是是有符合要求的

var reg2 = /c[a-z]*?d/g;

image20210106162113155.png

  • 贪婪模式:尽可能的选取多个【婪多个】
  • 惰性模式:尽可能少取以个【懒惰】

在禁止贪婪模式以后,就会变成惰性模式,它会尽可能的少重复

原子组编号

在我们之前学习原子组的时候我们学过,可以将规则通过()来形成一个分组,其实在形成分组的时候,默认会形成一个分组编号

var str = "<div></div>";
//现在希望编写一个正则去验证HTML的标签
//初步的思维
var reg1 = /<[a-zA-Z0-9]+><\/[a-zA-Z0-9]+>/;

在上面的正则表达式当中,我们表面看起来是已经完成了功能,但是有隐患,如果我们的字符串是<div></span>这个时候它也会验证成功,这是不符合要求的

我们要求的是开始标签名与结束标签名保持一致。我开始匹配的内容[a-zA-Z0-9]+要与后面匹配的[a-zA-Z0-9]+保持一致,这怎么办呢,这个时候就要使用到原子组编号

var reg2 = /<([a-zA-Z0-9]+)><\/\1>/;

后面正则表达式所出现的\1代表的是匹配出来的第1个分组内容

我们还可以通过下面的案例来学习分组

var str = "fdafffdaaklfjklja";
//现在要求找出字符串中连续重复的字符串
var reg = /(\w)\1+/g;
str.match(reg);  //这样就可以得到结果了

当然原子组的编号不仅仅只有1个,可以实现多个组的编号,如下

var str ="ababcdefaceced1212rtioio4ybyb";
//在里面找出那些有规律的两个一起重复的字符串
var reg =/(\w)(\w)\1\2/g;
str.match(reg);

分组不产生原子组编号

()分组产生分组号

(?:)分组不产生原子组编号

在正常情况下,原子组当中只要有了小括号就会在提取的时候产生编号,如下所示

var str = "今天是9月10日,唉呀!今天又跑到香港去玩了,买了好多东西,购物花了我¥346.77元,结果吃饭只能用美元,花了我$34.78元,坐港铁花了¥11元。";
var reg = /(?<=[\$¥])\d+(\.\d+)?/g;
reg.exec(str);
//得到结果如下

image20210913141612135.png

但是在这里,我们不需要这个编号,只是仅仅希望它们保持一个分组,所以就希望有一个技术是分组不编号

var reg = /(?<=[\$¥])\d+(?:\.\d+)?/g;

image20210913142248589.png

前瞻后顾

在弄清楚之个东西之前,先要让同学们知道什么是正则表达式的前后关系

在学习这个地方之前,还一定要弄清楚,

  1. 你的匹配条件是什么?
  2. 你的限制条件是什么?

例如:李强要找一个女朋友 ,但是限定条件是这个女的要有钱,漂亮,身高170CM以上

  1. 匹配条件:女
  2. 限定条件:有钱,漂亮,身高170CM
前瞻

前瞻(Look ahead positive): 匹配的是A,限制条件是A前面是B。

A(?=B)

想要匹配abc并且abc的前面是123的表达式,应该这样

image20210106172004702.png

负前瞻

负前瞻(Look ahead negative): 顾名思义,该正则匹配A,限制条件是A前面不是B

A(?!B)

想要匹配abc并且abc的前面不是123的表达式,应该这样:

image20210106172148524.png

后顾

后顾(Look behind positive ): 匹配表达式A,限制条件A的后面是B

(?<=B)A

想要匹配abc并且abc的后面是123的表达式

image20210106172636353.png

负后顾

负后顾(Look behind negative ): 匹配表达式A,限制条件是A的后面不是B

(?<!B)A

想要匹配abc并且abc的后面不是123的表达式,应该这样:

image20210106172754076.png

正则表达式的操作方法

方法 说明
String.prototype.replace() 字符串替换
String.prototype.search() 字符串查找
String.prototype.match() 字符串匹配提取
String.prototype.split() 字符串根据正则表达式分割
RegExp.prototype.test() 根据正则规则验证字符串
RegExp.prototype.exec() 根据正则表达式规则提取字符串

test()验证

这是正则表达式的一个方法,用于验证一个字符串是否符合正则的要求,如果符合就返回true,不符合就返回false

//正则表达式的验证是test方法,它是正则对象的方法
/**
     * 验证用户名,必须字母或下划线开头,后面接任意非空字符,总长度在6~10位
     * 验证年龄:必须是正整数
     * 验证性别:必须是男或女
     * 验证爱好:必须是 看书,睡觉,玩游戏三者之顺选择,可以选多个
*/

var reg1 = /^[a-z]\S{5,9}$/i;
reg1.test("a123abd");

var reg2 = /^\d+$/;
reg2.test("12");

var reg3 = /^[男女]$/;

//这个正则要去掉重复的情况
var reg4 = /(看书|睡觉|玩游戏)+/;

exec()提取

var str ="今天是9月10日,唉呀!今天又跑到香港去玩了,买了好多东西,购物花了我¥346.77元,结果吃饭只能用美元,花了我$34.78元,坐港铁花了¥10元。";
//在上面的字符串里面,我们要提取所有的金额(美圆和人民币)

第一种方式的操作:手动操作

//在上面的字符串里面,我们要提取所有的金额(美圆和人民币)
var str = "今天是9月10日,唉呀!今天又跑到香港去玩了,买了好多东西,购物花了我¥346.77元,结果吃饭只能用美元,花了我$34.78元,坐港铁花了¥11元。";
var reg = /(?<=[\$¥])\d+(\.\d+)?/g;
var result = "";
//第一次提取						//346.77
result = reg.exec(str);
console.log(result);
//第二次提取						//34.78
result = reg.exec(str);		
console.log(result);
//第三次提取						//11
result = reg.exec(str);
console.log(result);
//第四次提取						//null
result = reg.exec(str);
console.log(result);

第二种方式的操作:使用循环

var result = "";
while ((result = reg.exec(str)) != null) {
    console.log(result);
}

image20210913141612135.png

这一种正则表达式的提取是一种非常高级的提取方法,我们可以在复杂操作里面使用这个方法,但是一般简单的提取操作,我们会使用更简单的方法


match()提取

//match方法不是正则表达式对象的方法,它是字符串对象的方法
//match方法是匹配的意思,根据正则表达式来匹配
var str = "今天是9月10日,唉呀!今天又跑到香港去玩了,买了好多东西,购物花了我¥346.77元,结果吃饭只能用美元,花了我$34.78元,坐港铁花了¥11元。";
//在上面的字符串里面,我们要提取所有的金额(美圆和人民币)
var reg = /(?<=[\$¥])\d+(?:\.\d+)?/g;
//以前是使用正则表达式为主体,去操作字符串
//现在是以字符串为主体,去匹配正则
var result = str.match(reg);   //['346.77', '34.78', '11']

在上面的代码里面,我们可以看到,match方法与正则表达式对象里面的exec方法都是用于提取符要求的字符的,但是exec可以做到原子组的提取,而match则不行,如下所示

var str = "我的身份证号是425789199011150105,张三的身份证号是12355420050405233x";
    //写一个正则,提取字符串,并提取每个人性别组成对象
    /*
        [{
            IDCard:"425789199011150105",
            birthday:"1990-11-15",
            sex:"女"
        },{
            IDCard:"12355420050405233x",
            birthday:"2005-05-05",
            sex:"男"
        }]
    */

    var reg1 = /\d{17}[\dx]/g;
    var result1 = str.match(reg1); //['425789199011150105', '12355420050405233x']

    //上面明显不是最好的处理办法

    var reg2 = /\d{6}(\d{4})(\d{2})(\d{2})\d{2}(\d{1})[\dx]/g;
    var result2 = "";
    var arr = [];
    while ((result2 = reg2.exec(str)) != null) {
        var obj = {
            IDCard: result2[0],
            // birthday:result2.slice(1,4).join("-"),
            birthday: result2[1] + "-" + result2[2] + "-" + result2[3],
            sex: result2[4] % 2 == 0 ? "女" : "男"
        }
        arr.push(obj);
    }
    console.log(arr);

注意:无论是exec()还是match() 在目前阶段进行提取操作的时候,最后是加一个全局修饰符g

split()分割

字符串的split方法之前我们已经讲过了,它是通过某一个特定的字符去拆分字符串形成数组

var str = "get-element-by-id";
var arr = str.split("-");   //["get", "element", "by", "id"];


var str1 = "我们是123club组合我们的的年龄是19岁身高都在170公分以上";
//希望通过中间在数字去拆分 ["我们是","club组合哈组合我们的的年龄是","岁身高都在","公分以上"];
var reg1 = /\d+/;
var arr1 = str1.split(reg1);

search()搜索

之前在字符串里面,我们可以通过search查找某个字符串的索引位置,只是那个时候使用是indexOf

以前的情况下面

var str1 = "大家好,我是人见人爱的张三,我的身高是170CM!";
var str2 = "大家好,我是高富帅李四,我的身高是186CM!";
var str3 = "大家好,我是美女西施,我的身高是169CM!";
var str4 = "大家好,我是古典成熟高质量男性小乐乐,我的身高是178CM!";
//在上面的字符串当中,找所有的数字的位置
str1.indexOf("170");
str2.indexOf("186");
str3.indexOf("169");
str4.indexOf("178");
//这个参数在变化,如果这里有100个变量,我就有可能要写100句话
str1.search(/\d+/g);
str2.search(/\d+/g);
str3.search(/\d+/g);
str4.search(/\d+/g);
//这个时候的参数就固定成了一个正则表达式

replace()替换

它是字符串的方法

var str = "我爱你,你爱我吗?";
var str1 = str.replace("爱","恨");  //"我恨你,你爱我吗?";

replace方法默认情况下只会替换第一个匹配项,不会全局匹配,所以如果要全局匹配,则只能使用正则

var str = "我爱你,你爱我吗?";
var str2 = str.replace(/爱/g,"恨");		//"我恨你,你恨我吗";

当我们所查找替换的元素一旦是通过正则匹配的,则replace()第二个参数就变了,它变成了一个回调函数

var str = "我爱你,你爱我吗?";
str.replace(/爱/g, function (p) {
    // 第一个参数:p代表正册表达式的匹配项
    console.log(p);
});

当正则表达式里面有原子组的时候

var str = "大家好,今天是2021-01-06 14:48:30,今天天气不好,有点冷";
var reg = /\d{4}-(\d{2})-\d{2}\s(\d{2}):\d{2}:\d{2}/g;
str.replace(reg, function (p,g1,g2) {
    // 第一个参数:正则匹配的结果
    // 第后面开始的参数则代表原子组编号 group1   group2
    console.log(p);
    console.log(g1);
    console.log(g2);
})

image20210913152135000.png

上面的01就是原子组1的结果,02就是原子组2的结果

// 给一个连字符串例如:get-element-by-id转化成驼峰形(推荐使用正则表达式+replace的方式完成)
var str1 = "get-element-by-id";
//getElementById;
var str2 = str1.replace(/-([a-z])/g,function(p,g1){
    // console.log(p,g1);
    return g1.toUpperCase();
})

总结

  1. 正则只对字符串进行操作
  2. 正则的创建方式要弄清楚
  3. 正则的规则
    • 一元符
    • 原子组与原子表
    • 重复匹配
    • 原子组编号
    • 前瞻后顾
  4. 使用到正则的地方目前你们所接触只有String对象与RegExp对象

案例与练习

  1. 提取歌词,将歌词内容提取为一个数组,歌词时间提取为一个数组

    var musicLrc = `[00:00.000]少年 - 梦然 (Miya)
    [00:02.020]词:梦然
    [00:04.040]曲:梦然
    [00:06.060]编曲:张亮
    [00:08.090]制作人:张亮/徐阁
    [00:10.110]和声编写:海青/梦然
    [00:12.130]和声演唱:海青/梦然
    [00:14.160]Rap:梦然
    [00:16.180]混音工程师:赵靖
    [00:18.200]母带工程师:赵靖
    [00:20.230]监制:梦然
    `;
    
  2. 删除与某个字符相邻且相同的字符,比如 fdaffdaaklfjklja 字符串处理之后成为“fdafdaklfjklja"

    var str = "fdafffdaaklfjklja";
    var reg = /([a-z])\1+/g;
    /*
        var str2 = str.replace(reg, function (p, g1) {
            console.log(p, g1);
            return g1;   //返回第一个原子组
        })
    */
    var str2 = str.replace(reg,"$1");   //这里的$1相当于上面的g1也就是第一个原子组
    
  3. 正则表达式验证实例

    /*是否带有小数*/
    function    isDecimal(strValue )  {  
       var  objRegExp= /^\d+\.\d+$/;
       return  objRegExp.test(strValue);  
    }  
    
    /*校验是否中文名称组成 */
    function ischina(str) {
        var reg=/^[\u4E00-\u9FA5]{2,4}$/;   /*定义验证表达式*/
        return reg.test(str);     /*进行验证*/
    }
    
    /*校验是否全由8位数字组成 */
    function isStudentNo(str) {
        var reg=/^[0-9]{8}$/;   /*定义验证表达式*/
        return reg.test(str);     /*进行验证*/
    }
    
    /*校验电话码格式 */
    function isTelCode(str) {
        var reg= /^((0\d{2,3}-\d{7,8})|(1[3584]\d{9}))$/;
        return reg.test(str);
    }
    
    /*校验邮件地址是否合法 */
    function IsEmail(str) {
        var reg=/^\w+@[a-zA-Z0-9]{2,10}(?:\.[a-z]{2,4}){1,3}$/;
        return reg.test(str);
    }
    
  4. 分析百度网盘的连接

    /**
     * [reg 百度网盘链接匹配]
     * 说明:匹配支持百度分享的两种链接格式
     * 格式一:链接: https://pan.baidu.com/s/15gzY8h3SEzVCfGV1xfkJsQ 提取码: vsuw 复制这段内容后打开百度网盘手机App,操作更方便哦
     * 格式二:http://pan.baidu.com/share/link?shareid=179436&uk=3272055266 提取码: vsuw 复制这段内容后打开百度网盘手机App,操作更方便哦
     * 匹配出下载地址和提取码,并且还支持如果没有提取码,也能匹配出下载链接。
     * @type {正则表达式}
     * @return array 返回匹配成功的链接和地址
     */
    
    function baiduDownLinkArr( string ){
      var reg = /([http|https]*?:\/\/pan\.baidu\.com\/[(?:s\/){0,1}|(share)]*(?:[0-9a-zA-Z?=&])+)(?:.+:(?:\s)*)?([a-zA-Z]{4})?/;
      console.log(reg.exec(string));
    }
    
  5. 现有数字,请将数据转换成格式(推荐使用正则表达式+replace的方式完成),如12345678转换成12,345,678

    var str = "12345678";
    var reg = /\d{1,3}(?=(\d{3})+$)/g;
    /*
        var str1 = str.replace(reg, function (p) {
            // console.log(p);
            return p + ",";
        });
    */
    var str1 = str.replace(reg,"$&,");  //这里的$&就相当于正则表达式匹配的字符串,也就是上面回调里面的p
    

    其实在工作当中,还有一个更简单的办法,如下

    var str2 = (12345678).toLocaleString("en-US");
    console.log(str2);  //"12,345,678"
    
  6. 写一个正则表达式,匹配1~15之间的任意数

    var reg = /[1-9]((?<![2-9])[0-5])?/;
    

    写一个方法,提取出一段话中的人民币金额与美元金额。如下所示

    var str ="今天是9月10日,唉呀!今天又跑到香港去玩了,买了好多东西,购物花了我¥346.77元,结果吃饭只能用美元,花了我$34.78元,坐港铁花了¥10元。";
    
    var reg = /(?<=[\$¥])\d+(\.\d+)?/g;
    
  7. 给一个连字符串例如:get-element-by-id转化成驼峰形(推荐使用正则表达式+replace的方式完成)

     // 给一个连字符串例如:get-element-by-id转化成驼峰形(推荐使用正则表达式+replace的方式完成)
    var str1 = "get-element-by-id";
    //getElementById;
    var str2 = str1.replace(/-([a-z])/g,function(p,g1){
        // console.log(p,g1);
        return g1.toUpperCase();
    })
    
  8. dgfhfgh254.45bhku289fgdhdy675gfh获取一个字符串中的数字字符,并按数组形式输出。输出[254,289,675]

    var str = "dgfhfgh254.45bhku289fgdhdy675gfh";
    //提取数字,形成数组
    var reg = /\d+(\.\d+)?/g;
    var arr = str.match(reg);
    console.log(arr);
    
  9. 写一个正则表达式,匹配班级的学号从H12030001~H21039999

    var reg1 = /^H2103(?!0000)(\d{4})$/;
    var reg2 = /^H2103\d{4}(?<!0000)$/;
    
  10. 将字符串var str = "???"变成"?,?,?"这一种形式,使用正则表达式完成


评论