正则表达式
正则表达式并不是JavaScript里面独有的技术,基本上所有的编程语言都支持这个技术。
正则表达式有以下几个特点【它的特点也可以认为是它的使用场景】
- 正则表达式只对字符串进行操作
- 正则表达式做的是根据你所设定的规则对字符串进行“验证”,“提取”,“搜索”,“替换”等操作
- 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);
上面就是正则表达式的创建过程,在学习正则表达式的过程当中,我们先了解正则表达式的两个方法
test()
方法,用于验证某个字符串是否符合这个正则表达式规则exec()
方法,用于根据正则表达式去字符串中提取符合要求的字符
根据上面的两个方法,我们才可以学习正则表达式
同时要注意,正则表达式在创建的过程当中,它还可以添加修饰符flags
,主要的修饰符有3个
g
代表全局global
的意思i
代表ignore
忽略大小写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;
- 贪婪模式:尽可能的选取多个【婪多个】
- 惰性模式:尽可能少取以个【懒惰】
在禁止贪婪模式以后,就会变成惰性模式,它会尽可能的少重复
原子组编号
在我们之前学习原子组的时候我们学过,可以将规则通过()
来形成一个分组,其实在形成分组的时候,默认会形成一个分组编号
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);
//得到结果如下
但是在这里,我们不需要这个编号,只是仅仅希望它们保持一个分组,所以就希望有一个技术是分组不编号
var reg = /(?<=[\$¥])\d+(?:\.\d+)?/g;
前瞻后顾
在弄清楚之个东西之前,先要让同学们知道什么是正则表达式的前后关系
在学习这个地方之前,还一定要弄清楚,
- 你的匹配条件是什么?
- 你的限制条件是什么?
例如:李强要找一个女朋友 ,但是限定条件是这个女的要有钱,漂亮,身高170CM以上
- 匹配条件:女
- 限定条件:有钱,漂亮,身高170CM
前瞻
前瞻(Look ahead positive): 匹配的是A,限制条件是A前面是B。
A(?=B)
如想要匹配abc并且abc的前面是123的表达式,应该这样
负前瞻
负前瞻(Look ahead negative): 顾名思义,该正则匹配A,限制条件是A前面不是B
A(?!B)
想要匹配abc并且abc的前面不是123的表达式,应该这样:
后顾
后顾(Look behind positive ): 匹配表达式A,限制条件A的后面是B
(?<=B)A
想要匹配abc并且abc的后面是123的表达式
负后顾
负后顾(Look behind negative ): 匹配表达式A,限制条件是A的后面不是B
(?<!B)A
想要匹配abc并且abc的后面不是123的表达式,应该这样:
正则表达式的操作方法
方法 | 说明 |
---|---|
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);
}
这一种正则表达式的提取是一种非常高级的提取方法,我们可以在复杂操作里面使用这个方法,但是一般简单的提取操作,我们会使用更简单的方法
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);
})
上面的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();
})
总结
- 正则只对字符串进行操作
- 正则的创建方式要弄清楚
- 正则的规则
- 一元符
- 原子组与原子表
- 重复匹配
- 原子组编号
- 前瞻后顾
- 使用到正则的地方目前你们所接触只有
String
对象与RegExp
对象
案例与练习
-
提取歌词,将歌词内容提取为一个数组,歌词时间提取为一个数组
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]监制:梦然 `;
-
删除与某个字符相邻且相同的字符,比如
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也就是第一个原子组
-
正则表达式验证实例
/*是否带有小数*/ 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); }
-
分析百度网盘的连接
/** * [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)); }
-
现有数字,请将数据转换成格式(推荐使用正则表达式+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"
-
写一个正则表达式,匹配1~15之间的任意数
var reg = /[1-9]((?<![2-9])[0-5])?/;
写一个方法,提取出一段话中的人民币金额与美元金额。如下所示
var str ="今天是9月10日,唉呀!今天又跑到香港去玩了,买了好多东西,购物花了我¥346.77元,结果吃饭只能用美元,花了我$34.78元,坐港铁花了¥10元。"; var reg = /(?<=[\$¥])\d+(\.\d+)?/g;
-
给一个连字符串例如: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(); })
-
dgfhfgh254.45bhku289fgdhdy675gfh获取一个字符串中的数字字符,并按数组形式输出。输出[254,289,675]
var str = "dgfhfgh254.45bhku289fgdhdy675gfh"; //提取数字,形成数组 var reg = /\d+(\.\d+)?/g; var arr = str.match(reg); console.log(arr);
-
写一个正则表达式,匹配班级的学号从
H12030001~H21039999
var reg1 = /^H2103(?!0000)(\d{4})$/; var reg2 = /^H2103\d{4}(?<!0000)$/;
-
将字符串
var str = "???"
变成"?,?,?"
这一种形式,使用正则表达式完成