工作中用到正则的时候真的太多了,比如要撸java,你没有个正则验证某个东西,少说得多写七八行几十行代码,Linux中没有这东西,很多配置文件也得多个好几行,你去搞某些网站,没有正则脚本帮助你,那些命令敲死你都不为过,没办法呀!学呗,去b站找点视频慢慢扒拉着,完了写着写着就会了。
1.概念
regular expression:RegExp(用来处理字符串的规则),可以验证字符串时候符合某个规则(test),把字符串中符合规则的内容捕获到(exec/match)
2.正则表达式基础
创建方式有两种:字面量方式和构造函数方式
区别1:声明方式不同以及对转义符号的处理不同
let reg1=/\d+/; //字面量方式
let reg2=new RegExp("\\d+","i/m/g"); //构造函数方式有两个参数:(元字符字符串,修饰符字符串)
区别2:对正则中包含某个变量的值的处理不同
let type="fndd";
reg = /^@"+type+"@$/;
console.log(reg.test("@fndd@")); -->false //字面量方式不能包含某个变量的值
let type="fndd";
reg=new RegExp("^@"+type+"@$");
console.log(reg.test("@fndd@")); -->true //可以匹配
元字符:
1.量词元字符,设置出现的次数
* 代表0到多次
+ 代表1到多次
? 代表零次或一次
{n} 出现n次
{n,} 出现n到多次
{n,m} 出现n到m次
2.特殊元字符,单个或者组合在一起代表特殊含义的
\ 转义字符(普通到特殊或者特殊到普通)
. 除\n(换行符号)以外的任意字符
^ 以某一元字符作为开始
$ 以某一元字符作为结束
\d 0~9之间的数字
\D 非0~9之间的数字(小写和大写的意思相反)
\w 数字、字母、下划线中的任意一个字符
\s 一个空白字符(包含空格、制表符、换页符)
\t 一个制表符(TAB键:四个空格)
\b 匹配一个单词的边界
x|y x或者y中的一个字符
[xyz] x或者y或者z中的一个字符
[^xyz] 除过xyz以外的任意字符
[a-z] 指定a-z范围中的任意字符 [a-z0-9A-Z_]==\w
[^a-z] 除过a-z范围内的任意字符
() 分组符号,1.改变运算优先级2.分组捕获3.分组引用
(?:) 只匹配不捕获
(?=) 正向预查
(?!) 负向预查
3.普通元字符:代表本身含义的东西
\fndd\ 仅代表"fndd"字符串
修饰符:
i(ignoreCase) 忽略单词大小写匹配
m(multiline) 多行匹配
g(global) 全局匹配
3.元字符详细解析
用浏览器F12里面的console可以写下面的代码进行编码测试,真正了解每个元字符的含义,学完可以去正则表达式的网站去练习!
^ $ 开始结束
let reg =/^\d/; //数字开头
console.log(reg.test("2018fndd"));
console.log(reg.test("fndd2018"));
let reg =/\d$/; //数字结束
console.log(reg.test("2018fndd"));
console.log(reg.test("fndd2018"));
let reg =/\d/; //包含0-9
console.log(reg.test("2018fndd"));
console.log(reg.test("fndd2018"));
let reg =/^\d$/; //以数字开头以数字结束
console.log(reg.test("2018fndd"));
console.log(reg.test("fndd2018"));
练习:写一个匹配中国大陆所有手机号码的正则
let reg =/^1\d{10}$/; //简答版本
\ 转义字符
let reg =/^2.3$/; //代表匹配2开头中间任意字符3结尾的字符串
console.log(reg.test("23"));
console.log(reg.test("2@3"));
console.log(reg.test("2.3"));
let reg =/^2\.3$/; //只能匹配2.3这个小数
console.log(reg.test("23"));
console.log(reg.test("2@3"));
console.log(reg.test("2.3"));
let str =/\d/; --> false
reg=/^\d$/;
console.log(reg.test(str));
let str =/\d/; --> false
reg=/^\\d$/;
console.log(reg.test(str));
let str =/\\d/; --> false
reg=/^\d$/;
console.log(reg.test(str));
let str =/\\d/; --> false
reg=/^\\d$/;
console.log(reg.test(str));
let str ="\d"; --> false
reg=/^\d$/;
console.log(reg.test(str));
let str ="\d"; --> false
reg=/^\\d$/;
console.log(reg.test(str));
let str ="\\d"; --> false
reg=/^\d$/;
console.log(reg.test(str));
let str ="\\d"; --> true
reg=/^\\d$/;
console.log(reg.test(str));
x|y
let reg=/^18|29/;
console.log(reg.test("18")); --> true
console.log(reg.test("29")); --> true
console.log(reg.test("1829")); --> true
console.log(reg.test("182")); --> true
console.log(reg.test("829")); --> true
因为x|y运算优先级比较混乱,所以加分组()优化如下:
let reg=/^(18|29)$/; //只能匹配18或者29其中的一个
console.log(reg.test("18")); --> true
console.log(reg.test("29")); --> true
console.log(reg.test("1829")); --> false
console.log(reg.test("182")); --> false
console.log(reg.test("829")); --> false
[]
[]中括号中的字符大多数代表本身的意思
let reg=/^[@+]$/;
console.log(reg.test("@")); -->true
console.log(reg.test("+")); -->true
console.log(reg.test("@@")); -->false
console.log(reg.test("@+")); -->false
let reg=/^[\d]$/;
console.log(reg.test("\d")); -->false
console.log(reg.test("\\d")); -->false
console.log(reg.test("0")); -->true
let reg=/^[\\d]$/;
console.log(reg.test("\d")); -->true
console.log(reg.test("\\d")); -->false
console.log(reg.test("0")); -->false
let reg=/^[18]$/; //中括号中的数字代表的是单独的数字,而不是多位数
console.log(reg.test("1")); -->true
console.log(reg.test("8")); -->true
console.log(reg.test("18")); -->false
例子:写一个10~29之间的正则
let reg=/^[10-29]$/; //1或者0-2或者9 而不是10-29 错误写法
let reg=/^[(10-29)]$/; //同上,错误写法
let reg=/^[\(10-29\)]$/; //同上,错误写法
简单例子:
验证是否为有效数字
let reg=/^[+-]?(\d|([1-9]\d))(\.\d+)?$/;
验证密码为大小写或者6-16位
let reg=/^\w{6-16}$/;
验证真实姓名,汉字取值范围/^[\u4E00-\u9FA5]$/
let reg=/^[\u4E00-\u9FA5]{2,10}('[\u4E00-\u9FA5]{2,10}){0,2}$/; // '代表长名字中的点
验证邮箱
let reg=/^\w+((-\w+)|(\.w+))*@[a-zA-Z0-9]+((\.|-)[a-zA-Z0-9]+)*\.[a-zA-Z0-9]+$/;
验证身份证号码//前6地区 中间8位出生年月 最后4位abcd d是x或者是数字 c是数字,奇数男偶数女 ab是用其他所有16位算出来的
let reg=/^\d{17}(\d|X)$/; //错误
let reg=/^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/;
let reg=/^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$/;
4.正则的捕获
正则RegExp.prptotype上的方法
exec
test
字符串String.prototype上的方法
replace
match
splite
exec捕获
1.捕获的正则必须和字符串匹配,正确则输出数组,否则就输出null
2.每次执行一次exec只能捕获一次符合正则的内容,被称为正则捕获的懒惰性
let str="fangnidouding2018jianbo1987";
let reg=/\d/;
console.log(reg.exec(str)); -->["2", index: 13, input: "fangnidouding2018jianbo1987"]
console.log(reg.lastIndex(str)); -->0
//懒惰原因:字符串下标索引值没有被改变,每次都是从0位开始查找,一直到匹配上然后又回到0
let str="fangnidouding2018jianbo1987";
let reg=/\d+/g; //解决正则捕获的懒惰性给后面加全局修饰符g
console.log(reg.exec(str)); -->["2018", index: 13, input: "fangnidouding2018jianbo1987"]
console.log(reg.lastIndex); -->17
console.log(reg.exec(str)); -->["1987", index: 23, input: "fangnidouding2018jianbo1987"]
console.log(reg.lastIndex); -->27
console.log(reg.exec(str)); -->0
console.log(reg.lastIndex); -->null
console.log(reg.exec(str)); -->["2018", index: 13, input: "fangnidouding2018jianbo1987"]
console.log(reg.lastIndex); -->17
1.每次匹配完毕会改变下标的检索位置
2.字符串检索完之后会输出null,下标会循环到0,然后循环进行下一次检索
---------------------------------------------------------------------------------------------
test的匹配和捕获
let str="fangnidouding2018jianbo1987";
let reg=/\d+/g;
console.log(reg.test(str)); -->true
console.log(reg.lastIndex); -->17 //可以看出test匹配完之后会修改下标值
let str="{0}年{1}月{2}日";
let reg=/{(\d+)}/g;
console.log(reg.test(str)); -->true
console.log(RegExp.$1); -->0
console.log(reg.test(str)); -->true
console.log(RegExp.$1); -->1
console.log(reg.test(str)); -->true
console.log(RegExp.$1); -->2
console.log(reg.test(str)); -->true
console.log(RegExp.$1); -->2 //RegExp.$1~RegExp.$9取值范围,获取当前正则匹配后第一个到第九个分组的信息
--------------------------------------------------------------------------------------------
match捕获
let str="fangnidouding2018jianbo1987";
let reg=/\d+/g;
console.log(str.match(reg)); -->["2018", "1987"] //可以输出所有匹配规则的字符串
--------------------------------------------------------------------------------------------
let reg=/^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|X)$/; //?:的意思是只匹配不捕获,只拿有用的信息
let str="610527199311071107";
console.log(reg.exec(str));
console.log(str.match(reg)); -->["610527199311071107", "610527", "1993", "11", "07", "0", index: 0, input: "610527199311071107"]
--------------------------------------------------------------------------------------------
let str="book";
let reg=/^[a-zA-Z]([a-zA-Z])\1[a-zA-Z]$/; //()后面出现\1或者\任意数字的意思是此分组多引用一次
console.log(reg.test("good")); --ture
console.log(reg.test("keep")); --ture
console.log(reg.test("tomm")); --false
--------------------------------------------------------------------------------------------
正则捕获的贪婪性:
let str="fangnidouding2018jianbo1987";
let reg=/\d+/g;
console.log(str.match(reg)); -->["2018", "1987"] //每次捕获的时候,都是按照最长结果来匹配的
let str="fangnidouding2018jianbo1987";
let reg=/\d+?/g; //量词元字符后面加?含义是取消捕获的贪婪性
console.log(str.match(reg)); --> ["2", "0", "1", "8", "1", "9", "8", "7"]
-------------------------------------------------------------------------------------------
小结?的5大作用:
?左边是非量词元字符,代表出现0~1次
?左边是量词元字符,代表取消捕获的贪婪性
(?:)只匹配不捕获
(?=)正向预查
(?!)反向预查
-------------------------------------------------------------------------------------------
replace捕获
let str="fndd2018fndd1987";
str =str.replace(/fndd/g,"芳妮");
console.log(str); -->芳妮2018芳妮1987
将2018-01-02替换成2018年1月2日
let str="2018-01-02";
let reg=/^(\d{4})-(\d{1,2})-(\d{1,2})$/;
str=str.replace(reg,"$1年$2月$3日");
console.log(str);
//还可以用[str].replace([reg],[function])来处理
let str="2018-01-02";
let reg=/^(\d{4})-(\d{1,2})-(\d{1,2})$/;
str=str.replace(reg,(big,$1,$2,$3)=>{
console.log(big,$1,$2,$3); -->2018-01-02 2018 01 02
});
console.log(str);
//优化如下
let str="2018-01-02";
let reg=/^(\d{4})-(\d{1,2})-(\d{1,2})$/;
str=str.replace(reg,(...arg)=>{
let [,$1,$2,$3]=arg
$2.length<2?$2="0"+$2:null;
$3.length<2?$3="0"+$3:null;
return $1+"年"+$2+"月"+$3+"日";
});
console.log(str);
记得多练习,多练习在多练习,最后附上在线测试网址
https://tool.oschina.net/regex/