正则表达式基础
特殊字符
- . 表示匹配任意字符
- ^ 表示字符串的开始
- $ 表示字符串的结束
转义符
- \n 匹配换行符
- \r 匹配回车符
- \t 匹配制表符,即缩进
- \s 匹配任何空白字符, 包含空格、回车、换行、制表等
- \S 匹配任何非空白字符
- \b 匹配单词边界,有空白才有边界
- \B 匹配非单词边界
- \d 匹配数字
- \D 匹配非数字
- \w 匹配字母、数字、下划线,等价于'[A-Za-z0-9_]'
- \W 匹配非字母、数字、下划线。等价于 '[^A-Za-z0-9_]'
- \u 匹配unicode编码
转义字符
\ 转义字符可以将特殊字符转化为普通字符
字符集
[]: 匹配一个字符, [] 内写范围
[^]:取反,匹配该范围之外的
匹配中文:[\u4e00-\u9fa5]
量词
-
- 零个或多个
-
- 一个或多个
- ?零个或一个
- {n} 匹配n个
- {n,} 匹配>=n个
- {n,m} 匹配n到m个
或者
| 表示多个规则之间任选其一
子表达式
() 单独的一个规则
标识位
- i 忽略大小写
- g 全局匹配
- m 匹配多行
var reg = /\d+/gim;
练习
- 匹配手机号
^1\d{10}$
- 姓名3-6位
^[\u4e00-\u9fa5]{2,6}$
-
密码6-12位,数字、字母下划线
^\w{6,12}$
^\w+@\w+(\.\w+){1,2}$
-
座机号:xxxx-xxxx
^\d{4}-\d{7}$
-
正数(正小数、正整数)
^\d+(\.\d+)?$
-
小数(正负小数)
^[\d|-\d]+(\.\d+){1}$
-
整数(正负整数)
^[\d|-\d]+$
RegExp
创建正则表达式的方式
- 字面量
var reg = /\w+/gi;
- 构造函数
var reg = new RegExp(/\w+/,"gi");
var reg = RegExp(/\w+/, "gim");
正则实例
对象.属性(实例属性),实例属性是属于某个实例对象的私有的属性
类.属性(静态属性),静态属性是所有对公有的属性
静态属性
- RegExp.prototype.global:判断是否开启全局匹配,返回boolean,只读
- RegExp.prototype.ignoreCase:是否开启忽略大小写,返回boolean,只读
- RegExp.prototype.multiline:是否开启多行匹配,返回boolean,只读
- RegExp.prototype.source:返回该正则表达式
静态方法
- RegExp.prototype.test:测试某个字符串是否满足规则, 如果匹配到字符串返回true, 反之为false
var reg = /^(\d|-\d)+$/g;
console.log(reg.test('120'));
注意点:当是全局匹配时,test()函数会从上一次匹配的结束处重新开始匹配
// lastIndex是RegExp的静态属性, 表示匹配的开始位置
var reg = /abc/g;
console.log(reg.lastIndex, reg.test('124abc123abc123'))
console.log(reg.lastIndex, reg.test('124abc123abc123'))
console.log(reg.lastIndex, reg.test('124abc123abc123'))
console.log(reg.lastIndex, reg.test('124abc123abc123'))
console.log(reg.lastIndex, reg.test('124abc123abc123'))
console.log(reg.lastIndex, reg.test('124abc123abc123'))
打印结果
可以看出使用test()函数匹配是循环的,每匹配一次,第二次从上一次匹配结束的下一位进行匹配,直到匹配结束,再从头开始
利用这个特点test函数这个特点,我们可以得到目标字符串内有多少个符合正则表达式的字串,请看下面代码:
var reg = /abc/g;
var i = 0;
while(reg.test('124abc123abc123')){
i++;
}
console.log(i); // 2
贪婪模式
正则表达式默认情况,使用贪婪模式,即尽可能多的匹配
如果想不使用贪婪模式,需要在量词后添加?
例
var reg = /\d+?/g; // 尽可能匹配少的数字
- RegExp.prototype.exec():execute, 执行匹配,返回匹配结果,如果匹配不到返回null
exec()函数的返回值包含:匹配到的目标字符串,匹配的起始地位,如下:
var s = '123abc123abc123abc';
var reg = /\d+/g;
字符串中与正则相关的方法
String.prototype.match(reg):返回匹配结果
全局匹配时,返回匹配到的结果数组
var str = '121aba124aba1213aba';
var reg = /\d+/g;
console.log(str.match(reg))// ["121", "124", "1213"]
非全局匹配返回值与exec()相同
String.prototype.search(reg):返回第一次匹配的下标
String.prototype.split(reg, num):按照正则表达式内的字符分割,返回分割结果数组;num表示取分割结果的前几位
var str = '1234 djksfj sdklfj sdkfj'
var reg = /[ f]+/g; //按照制空格和字母f进行分割
console.log(str.split(reg)); // ["1234", "djks", "j", "sdkl", "j", "sdk", "j"]
String.prototype.replace(reg, func/str): 替换
replace第一个参数写正则表达式,第二参数可以为字符串也可以为函数
第二个参数为函数时:函数的第一个参数匹配到的字符串
例:将"hello word"转换为小驼峰命名
var str = '\tjavascript\nvue react es6 webpack css3 html5'
var newStr = str.replace(/\s*\b[a-z]/g, function (match) {
if(str.match(/\s*\b[a-z]/)[0] === match) return match.trim();
return match.toUpperCase().trim();
})
console.log(newStr) // javascriptVueReactEs6WebpackCss3Html5
练习
- 匹配任意一个字符串,得到匹配到的次数和匹配到的结果
- 得到一个字符串中中文字符的数量
- 过滤一敏感词,有一个敏感词数组,将给定字符串中的敏感词替换为****
- 得到一个html字符串中出现章节的数量
var str = '123213';
var reg = new RegExp(/\d/g,'g');
var i = 0;
var res;
while(res = reg.exec(str)){
i++;
console.log(res[0])
}
console.log(`共匹配${i}次`)
var str = '1大解放路口空间的萨拉克服2十大科技风凉开水3213';
var reg = new RegExp(/[\u4e00-\u9fa5]/g,'g');
var i = 0;
while(reg.test(str)){
i++;
}
console.log(`共有${i}个中文字符`)
// 实现方式1
var str = '1大解放路口空间的萨拉克营销服jiao yi2十大科技风毒凉du开水3213';
var arr = [ '营销', 'du', '毒','jiao yi'];
for(var i = 0; i < arr.length ; i++){
str = str.replace(new RegExp(arr[i],'g'), '****');
}
console.log(str)
// 实现方式2
var str = '1大解放路口空间的萨拉克营销服jiao yi2十大科技风毒凉du开水3213';
var arr = [ '营销', 'du', '毒','jiao yi'];
str = str.replace(RegExp(`(${arr.join('|')})+`, 'g'), '****');
console.log(str)
var html =`<P>第1章</P><P></P>第2章<P></P><P>
</P>第3章
<P></P><P>第4章</P><P></P>第5章<P></P>第100章<P></P>第7章<P></P>`
var reg = /第\d+章/g;
var i = 0;
while(reg.test(html)) i++;
console.log(`共${i}个章节`)
正则表达式高级用法
捕获组
小括号包裹的部分叫捕获组,捕获组会出现在匹配结果中
RegExp.prototype.exec()中的捕获组
示例代码
var str = '123abc-789ab-456abc';
var reg = /(\d)+\w+/g;
var res;
while(res = reg.exec(str)){
console.log(res)
}
每个被红色方框圈起来的部分就是每一个捕获组的捕获值,也就是我们正则表达式中(\d)匹配到的值。也就是说在一次匹配中捕获组捕获到的值,会从索引1向后依次排列
那么捕获组有什么用呢?看下面的例子
现在有一个需求,给定一个字符串为'2000-10-10,2019-10-14,2019-11-18', 让我们输出每一个日期,并且输出每一个年月日
var str = '2000-10-10,===2019-10-14/2019-11-18';
var reg = /(\d{4})-(\d{1,2})-(\d{1,2})/g;
while(res = reg.exec(str)){
console.log(`${res[0]} ${res[1]} ${res[2]} ${res[3]}`)
}
// 2000-10-10 2000 10 10
// 2019-10-14 2019 10 14
// 2019-11-18 2019 11 18
打印结果,可以看出我们不仅能得到每次匹配的字符串,还可以得到具体匹配的字符串的字串,这就是捕获组的真正作用
具名捕获组
我们还可以给每个捕获组命名
命名方式:在每个()内部的最前面写?<name>
,如
// 分别给每个捕获组命名为 year month day
var reg = /(?<year>\d{4})-(?<month>\d{1,2})-(?<day>\d{1,2})/g;
可以通过exec()函数的返回值group对象获取,让我们利用这个特性修改上述代码,可以得到相同的代码
var str = '2000-10-10,===2019-10-14/2019-11-18';
var reg = /(?<year>\d{4})-(?<month>\d{1,2})-(?<day>\d{1,2})/g;
while(res = reg.exec(str)){
console.log(`${res[0]} ${res.groups.year} ${res.groups.year} ${res.groups.year}`)
}
让我们再次打印exec函数匹配结果
可以发现groups内有出现了我们的捕获组,我们发现,原来groups保存的是具名捕获组的值
非捕获组
捕获组的执行是浪费效率的,但捕获又是默认,如果不是特殊需要我们只是想把()内的正则表达式作为一个整体,不想使用捕获功能,那么就需要用到非捕获组
触发方式:在每个()内部的最前面写?:
,如
var reg = /(?:\d{4})-(?:\d{1,2})-(?:\d{1,2})/g;
这样我们就不会再exec执行结果看见捕获组了
String.prototype.replace()中的捕获组
- 函数形式-replace第二个参数
var str = '2000-10-10-4,===2019-10-14-5/2019-11-6-18~2019-11-18';
var reg = /(\d{4})-(\d{1,2})-(\d{1,2})-(\d{1,2})/g;
str.replace(reg, function (match, g1, g2, g3, g4,index, self) {
console.log(...arguments)
})
// 这里正则表达式中有四个捕获组
// 函数从第1个到第1+4个参数就为捕获组捕获到的值
// 剩下两个参数为匹配的起始位置和输入的字符串
- 字符串形式-replace第二个参数
捕获组用$1、$2、$3
表示,如
var str = '2000-10-10-4,===2019-10-14-5/2019-11-6-18~2019-11-18';
var reg = /(\d{4})-(\d{1,2})-(\d{1,2})-(\d{1,2})/g;
str.replace(reg, '年$1月$2日$3小时$4')
反向引用
正则表达式中使用某个数组,使用方式:\捕获组编号
或\k<name>
查找出'aaaaabbbbbddddddddddcccccdewdddkkkk'中出现的连续字符
// 非具名捕获组
var str = 'aaaaabbbbbddddddddddcccccdewkkkk';
var reg = /(\w)\1+/g;
while(res = reg.exec(str)){
console.log(res[1])
}
// 具名捕获组
var str = 'aaaaabbbbbddddddddddcccccdewkkkk';
var reg = /(?<char>\w)\1+/g;
while(res = reg.exec(str)){
console.log(res.groups.char)
}
正向断言-预查
检查某个字符串是否满足某个规则,该规则不成为匹配结果也不成为捕获组
这个规则
的就叫正向断言,使用方式为(?=)
面试题
用正则表达式将'1123123123123'转换为'1,123,123,123,123'
var str = '123123123123'
var reg = /\B(?=(\d{3})+$)/g; // 断言内的所有()都不会成为匹配结果与捕获组
console.log(str.replace(reg, ','));
反向断言-预查
检查某个字符串是否满足某个规则,该规则不成为匹配结果也不成为捕获组
这个规则
的就叫反向断言,使用方式为(?!)
实际应用题
密码强度判断
function jugdePWD(pwd){
if(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*.]).{6,12}$/.test(pwd)){
return '强'
}else if(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{6,12}$/.test(pwd)){
return '中'
}else if (/^(?=.*[a-z])(?=.*[A-Z]).{6,12}$/.test(pwd)){
return '弱'
}else {
return '不满足条件'
}
}
console.log(jugdePWD('AAAAAi1$'));