js-14 正则篇
一、正则RegExp
用特殊的字符组成验证字符串的规则,用来检索或者替换字符串,检测表单输入的信息是否正确,默认只匹配一次;
正则:也叫做规则,让计算机能够读懂人类的规则(正则都是操作字符串的);
用处:比如注册,邮箱的验证,表单提交等;
Reg 正则 Exp表达式
正则下面的方法:exec
、test
字符串下面的方法:search
、match
、replace
、split
常用:test
、search
、match
、replace
;
二、创建规则
①构造函数创建:new RegExp(表达式,修饰符)
;一般用在需要用变量的时候
②字面量创建:/表达式/修饰符
例如: var a=/**/
;
var re = /a/g // a为字符串 推荐使用
var re = new RegExp(a) // a为变量
var re = new RegExp("a") // a为字符串
三、正常检测方法
检测str中是否有符合reg规则的字符串
test()
;reg.test(str);有返回true,没有返回false;exec()
;reg.exec(str);有返回数组,没有返回null
exec
可以循环匹配,放在while循环,每次循环从下一次开始查找,当找不到时会返回null,循环内部通过RegExp.$1获取小括号内容,$2为第二个小括号,以此类推;
var str = '?name=hny&age=18'
var o = {}
var res;
let reg = /(\w+)=(\w+)/g
while(res = reg.exec(str)){
// o[res[1]] = res[2]
o[RegExp.$1] = RegExp.$2
}
console.log(o)
注意:正则需要用变量保存, 不能直接在while使用,否则获取不到上一次,如:
while(/(\w+)=(\w+)/g.exec(str)){} // 报错;
四、修饰符
i
:不区分大小写,默认区分大小写g
:匹配全局,默认只匹配一次s
:不匹配.点的规则,解决\n \r,点默认不匹配换行符,加上可以匹配换行符u
:根据unicode解析字符(影响\w)
如果既不区分大小写也不区分一次,则加ig
;
五、正则中的元字符
使用特殊字符表示特殊含义
\ 转义符,转义表示本身;
需要转义的符号:[ ] { } ( ) . ? * + ^ \ / | ;
本来这个字符它有自身的意思,但是加上反斜杠就成了另外一个意思;
比如:'anb',这里表示anb,'a\nb' 这里表示a换行b;
元字符 | 说明 |
---|---|
\s | 空格:(空格、制表符、换行符) |
\S | 非空格 |
\d | 数字 |
\D | 非数字,同[^0-9]相同 |
\w | 字符(字母和数字及下划线) |
\W | 非字符 |
. | 除了换行符以外的任意字符, 加s还可以匹配换行符 |
n* | 匹配零个或多个n |
n+ | 匹配至少一个n的字符串 |
n? | 匹配零个或一个n |
^ | 行首匹配 |
$ | 行尾匹配 |
` | ` |
\n | 匹配换行符 |
\r | 匹配回车字符 |
\t | 匹配制表符 |
\f | 换页符 |
\b | 匹配一个单词边界,也就是单词和空格间的位置 |
\B | 匹配非单词边界,与上一个例子反过来 |
/[\u4e00-\u9fa5]/ | 匹配汉字 |
六、正则中的小括号
- ()里面的内容表示捕获分组,()会把每个分组里的匹配结果保存起来,使用$n获取;
/(a)(b)(c)\1\2\3/
\1代表a和小括号a是一样的内容、\2代表b,(xyz)+
匹配至少一个(xyz),括号表示一体,/a(b|c)d/
匹配abd,acd;hehe{3}
是e匹配3次,(hehe){3},是hehe单词匹配3次;
\1
或 $1
匹配第一个分组中的内容
\2
或 $2
匹配第二个分组中的内容
\3
或 $3
匹配第三个分组中的内容
\n
只能用在正则表达式中,$n
只能用在正则表达式之外的地方;
'2018-02-11'.replace(/(\d{4})\-(\d{2})\-(\d{2})/g,'$1/$2/$3') // '2018/02/11'
'18782832656'.replace(/(\d{3})(\d{4})(\d{4})/,'$1****$3')
let str = 'abbkejsbcccwqaa'
str.match(/(\w)\1*/g); // ['a','bb','k'.....]
// 判断双花括号的内容
let reg = /\{\{(.*)\}\}/
reg.test(str)
// 返回true或false,可以通过RegExp.$1来获取上一次匹配的结果;
RegExp.$1
第一个分组; 获取第一个分组有小括号的分组的匹配结果;
手机中间的*
var str = '13388889999';
var re = /(\d{3})(\d{4})(\d{4})/ 利用函数;
str.replace(re,function($0,$1,$2,$3){
return $1 + '****' + $3;
}
也可以:
var str = "18210399098";
var reg = /\d{4}/ig;
reg.lastIndex = 3; //从第三位开始匹配;
console.log(str.replace(reg.exec(str), "****"));
具名组匹配
为每组匹配指定名字(?<Groupname>)
const time = '2017-09-11'
const reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u
time.replace(reg,'$<day>/$<month>/$<year>')
let result = reg.exec(time)
console.log(result.groups) // 输出试试
如果需要具名组引用:使用\k<name>
/(?<word>[a-z])\k<word>/
格式化时间
let reg = /(\d{4})\D(\d{2})\D(\d{2}).*(\d{2}):(\d{2}):(\d{2})/gi
console.log(new Date().toLocaleString().replace(reg,'$1-$2-$3 $4:$5:$6'))
七、正则中的中括号
字符类:一组相似的字符,
[abc]
整体代表一个字符,内部为或的关系。
[^abc]
:排除abc;中括号中使用^,表示排除;
[a-z]
: 范围a-z;可以是[e-j]
;代表一个字符
八、正则中的大括号(量词)
{0,}
简写n*
匹配零个或多个n
{1,}
简写n+
匹配至少一个n的字符串;
{0,1}
简写?
匹配零个或一个n
{n}
匹配n次
{n,m}
匹配n到m次
{n,}
至少匹配n次;
九、正则中字符串的方法
replace
字符串.replace(正则,新的字符)
;将正则匹配到的字符更换新字符;
- 第二个参数也可以是一个函数:函数第一个参数是正则匹配成功的字符,通常是$0,
- 函数的参数:
- 第一个参数为匹配成功的字符$0;
- 第二个参数:正则的第一个小括号$1;
- 第三个参数:正则的第二个小括号$2;以此类推;
- 函数的参数:
- 第二个参数字符串的$符作用:
$1、$2、...、$99 | 与 regexp 中的第 1 到第 99 个子表达式相匹配的文本。 |
---|---|
$& | 与 regexp 相匹配的子串。当不加小括号就使用$& |
$' | 位于匹配子串左侧的文本。 |
$` | 位于匹配子串右侧的文本。 |
$$ | 直接量符号。 |
例子:分组操作,匹配子项;
var str = '2019-4-17';
var re = /(\d+)(-)/g;
str.replace(re,function($0,$1,$2){
//$0匹配成功的字符,$1匹配第一个小括号(子项)的,$2匹配第二个小括号(子项);
$2 //为第二个小括号的匹配项,为-,可以修改这个项;
}
str.replace(reg,function(cc){console.log(cc)});
str配合正则使用,正则加g,有多少个则替换多少个,如果正则加了g,使用这个方法和forEach是一样的;
// 例子1
var str = "北京,新疆,湖南,黑龙江,乌鲁木齐";
var reg = /北京|黑龙江|乌鲁木齐/g
var aa = str.replace(reg,function(val){ //循环替换里面的关键字 相当于执行了3遍
console.log(val);
var stm = "";
for(var i=0;i<val.length;i++){ //循环文字的长度,将reg替换掉;
stm+="*";
}
return stm;
});
console.log(aa); // **,黑龙江,新东方,****,西藏
// 例子2 转换驼峰命名法;
const toCameLize = str.replace(/-(\w)/g,(_,c)=>(c ? c.toUpperCase() : ''))
console.log(ab-cd-ef) // abCdEf;
split
str.split(reg)
;以符合正则的内容分割
第二个参数可选的,新数组的长度;
var str = '2019-4-17 15:25:25';
var re = /-|\s|:/g; // 以-或\s或:分隔
match
如果有就返回匹配到的字符组成的数组,没有就返回null;
默认只匹配一次,匹配多次可以加g
str.match(reg)
;
var str = "123hdsajkfd4355jkdslaf888"
str.match(/\d+/g); //默认查找一次 g查找全部
输出之后:ary[‘123’,’4355‘,’888‘] //输入全部数字;
var arr = ['book','slice','root','dlick'];
var arr1 = arr.filter(value=>value.match(/oo/g));
search
返回第一次出现的下标;如果没有,则返回-1;像字符串的indexOf()
字符串.search(reg)
;
lastIndex
纪录下一次要查找的位置的下标
正则.lastIndex
;
查找完为null之后从0开始,用正则调用,需要配合修饰符g;如果前面为1,则下一次为2;
var str = "18210399098";
var reg = /\d{4}/ig;
reg.lastIndex = 3; //从第三位开始匹配;
console.log(str.replace(reg.exec(str), "****"));
十、高级用法
前瞻(?=)、后顾(?<=)、负前瞻(?!)、负后顾(?<!)
// 前瞻:
e(?=f) e后边是f就匹配 f不会放进结果里; ?:则会放进结果里;
// 负前瞻:
e(?!f) e后边不是f就匹配 ?!f 将=换成! 则为取非;
// 后顾:
(?<=f)e e前面是f就匹配
// 负后顾:
(?<!f)e e前面不是f就匹配
例子
let a = '123ab'
a.match(/\d+(?=ab)/) // 123 ab不会放进结果里
a.match(/\d+(?:ab)/) // 123ab ab会放进结果里
a.match(/\d+(?!bc)/) //123 后面不能是bc,bc是不会放入到匹配结果内的;
a.match(/(?<=\d)[a-z]+/) //ab 匹配紧跟着数字后的字母;
组合使用
//匹配字母之间的数字;
let a = 'qee132ab';
a.match(/(?<=[a-z]+)\d+(?=[a-z]+)/) // ?=匹配为ab的;
//["132", index: 3, input: "qee132ab", groups: undefined]
组合符:?:
理解?:需要理解捕获分组和非捕获分组的概念
()
表示捕获分组,()会把每个分组里的匹配的值保存起来,使用$n(n是一个数字,表示第n个捕获组的内容)
(?:)
表示非捕获分组,和捕获分组唯一的区别在于,非捕获分组匹配的值不会保存起来
例子:格式化金钱
// 匹配的是后面3*n个数字的非单词边界(\B)
console.log('12345678'.replace(/\B(?=(?:\d{3})+(?!\d))/g, ','))
/* 原理解析:
* 查找3n正整数和1个非数字, 8后面没有数字了, 因此需要加一个非数字
* 其实不加?:也行,但大多数加上
*/
组合符:.*
(.*)
匹配直到后面条件的最后一个;
(.*?)
匹配到后面条件下一个符合条件;
贪婪模式与非贪婪模式
贪婪模式 :正则表达式在匹配的时候默认会尽可能多的匹配
非贪婪模式 :通过在限定符后加?
叫非贪婪匹配;
比如:\d{3,6}
默认匹配6个数字而不是3个,\d{3,6}?
,匹配三次;
'abbbb'.replace(/ab+/,'-') //则结果是'-';
'abbbb'.replace(/ab+?/,'-') // 则结果是'-bbb';
// 大家好,我叫{{name}},今年{{age}}岁
/{{(.*)}}/ // 我叫{{直到}}岁; 贪婪模式匹配到结尾;
/{{(.*?)}}/ // 我叫{{}},今年{{}}岁; 添加?开启非贪婪模式;
十一、常用正则
var reg = {
phone: /^1\d{10}$/
qq : /^[1-9][0-9]{4,9}$/
email : /^\w+@\w+\.(com|cn|com.cn|wang|cc|xin|ren|info|xyz|site|club|win|net|org)$/
space : /^\s*|\s*$/g
身份证:/^[1-9]\d{14}|[1-9]\d{17}|[1-9]\d{16}x$/
匹配中文:/[\u4e00-\u9fa5]/g
固话:/^0\d{2,3}-[1-9]\d{6,7}$/
网址:/^[a-zA-Z]+:\/\/[^\s]+$/
密码:/^(?=.*[a-zA-Z])(?=.*[1-9])(?=.*[\W]).{6,}$/;
格式化金钱:/\B(?=(\d{3})+(?!\d))/g,
}
密码限制
(?=)
零宽断言,表示有;(?=.*[a-zA-Z])
后面必须有一位大写或小写字母(?=.*\d)
后面必须有一位数字(?=.*\W)
后面必须有一个非字母数字及下划线的特殊符号
后面必须跟点再写限制量词;
.*
表示匹配除了换行符以外的任意0个或多个;
必须包含字母 数字 及特殊符号,且6位以上
/^(?=.*[a-zA-Z])(?=.*[1-9])(?=.*[\W]).{6,}$/
不能全部是数字或字母或符号
后面的一个点表示匹配一位换行符以外的字符,带了大括号匹配6位即可;
大小写字母 数字 特殊符号 必须包含两种
/^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)])+$).{6,}$/
至少有一个并且数字开头
/\d+[a-z]+/
匹配到汉字以及前面的字符
/[a-z0-9]*[\u4e00-\u9fa5+]/g.exec(this.innerHTML)
十二、正则示例
查找包含特定单词的句子
const str = "The apple tree originated in Central Asia. It is cultivated worldwide. Apple matures in late summer or autumn."
// 查找包含单词“ apple”的句子
str.match(/[^.!?]*\bapple\b[^.!?]*.?/gi)
// 输出结果
// => ["The apple tree originated in Central Asia.", "Apple matures in late summer or autumn."]
从文件中去除无效字符
const str = "https://en.wikipedia.org/"
str.replace(/[<>|:"*?\\/]+/g, '')
// => "httpsen.wikipedia.org"
用单个空格替换多个空格
const str = " My opinions may have changed, but not the fact that I'm right."
str.replace(/\s\s+/g, ' ')
// => " My opinions may have changed, but not the fact that I'm right."
此代码用空格(U + 0020)
字符替换任何类型的空格字符,包括ASCII空格,制表符,换行符,回车符,垂直制表符和换页符。因此,如果回车符紧跟在制表符之后,它们将被空格替换。如果这不是我们的意图,并且只想替换相同类型的空格,请改用以下代码:
str.replace(/(\s)\1+/g, '$1').trim(); // 需要.trim 去除结尾跟开头的空格
限制用户只能输入数字或字母
const input1 = "John543";
const input2 = ":-)";
/^[A-Z0-9]+$/i.test(input1); // → true
/^[A-Z0-9]+$/i.test(input2); // → false
将网址变成链接
const str = "Visit https://en.wikipedia.org/ for more info.";
str.replace(/\b(https?|ftp|file):\/\/\S+[\/\w]/g, '<a href="$&">$&</a>')
// => "Visit <a href="https://en.wikipedia.org/">https://en.wikipedia.org/</a> for more info."
删除重复的单词
const str = "This this sentence has has double words."
str.replace(/\b(\w+)\s+\1\b/gi, '$1')
// => "This sentence has double words."
清除开始结束标签的空格
var trim = /^\s*|\s*$/g