平时在使用一些正则都是网上拷贝下来的,知其用而不知道所以然,现在发现得好好学习一下正则表达式了。
在学习正则之前,我们得先了解一些正则的元字符
RegExp对象
声明一个正则有两种写法
let re = /[\d]+/g;
let re1 = new RegExp('[\\d]+', 'g');
// es6使用构造函数创建正则,可以执行修饰符
let re2 = new RegExp(/\w+/g, 'i'); // /\w+/i
两者的区别就是,new RegExp
的第一个参数字符串的转义字符要两个斜杠,第二个参数是修饰符
- i 执行对大小写不敏感的匹配
- g 执行全局匹配
- m 执行多行匹配
- y 执行全局匹配,后一次匹配从上一次匹配成功的下一个位置开始(es6新增的修饰符)
var str = 'aaa_aa_aa';
var reg = /a+/g;
reg.exec(str); // [aaa]
reg.exec(str); // [aa]
reg = /a+/y;
reg.exec(str); // [aaa]
reg.exec(str); // []
正则方法
正则对象有三个方法,分别为test()
,exec()
,compile()
- test()检索字符串是否有对应的值,返回true或false
比如检查input输入是否合法,不需要获取匹配值,可以使用test()
方法
let reg = /[\w]+/; // 只能是数字或者字符串和下划线
reg.test('肯德基'); // false
reg.test('aa_123'); // true
- exec()检索字符串的指定值,返回null或者数组
比如我们要获取匹配的指定值,可以使用exec
/abc/.exec('a'); // null
/abc/.exec('dcba abc qwer abc'); // 匹配第一个就停止,返回 ['abc']
如果需要匹配子项的话
/(ab)c/.exec('dcba abc qwer abc'); // 返回一个数组,第二个参数是括号的内容 ['abc', 'ab']
上面的例子匹配到第一个就会停止了,如果要继续匹配,可以使用while循环,正则需要加上g修饰符,如果没有会死循环
let re = /abc/g, ret;
while (ret = re.exec('dcba abc qwer abc')) {
console.log(ret)
}
// 分别打印出以下结果,由于使用了全局匹配,会返回额外的index和input结果
// ["abc", index: 5, input: "dcba abc qwer abc"]
// ["abc", index: 14, input: "dcba abc qwer abc"]
- compile()可以执行,改变和重新编译正则表达式
该方法可以复用一个对象,在代码执行的过程中重新改变匹配方式,用的地方相对较少
正则应用
这里举一个例子,例如需要校验用户名是否符合需求
需求:用户名以字母开头+数字/字母/下划线,长度不小于6不大于12
分析:
- 以字母开头的正则
^[a-zA-Z]
- 数字/字母/下划线的正则
\w ==> [a-zA-Z0-9_]
- 由于首字母占用一位,剩下5-11个长度,
{5,11}$
通过上面的分析,可以合并为一个表达式:/^[a-zA-Z]\w{5,11}$/
贪婪模式
贪婪模式会匹配尽可能多的字符,比如
let ret = /a+/.exec('aaaaaa') // ["aaaaaa", index: 0, input: "aaaaaa", groups: undefined]
上面的正则会尽量去匹配多个字符,结果返回的是 aaaaaa
非贪婪模式
非费贪婪模式会尽量少的匹配,只需要在修饰符后面加上?
即可
// 很懒,只匹配到一个就停止了
let ret = /a+?/.exec('aaaaaa') // ["a", index: 0, input: "aaaaaa", groups: undefined]
非贪婪模式的使用场景,比如有这样的html字符串
let html = "<div>div1</div><div>div2</div>"
我们想取<div>div1</div>
字符串,获取你想这样写
let ret = /<div>.*<\/div>/.exec(html) // "<div>div1</div><div>div2</div>"
结果返回的是整个html字符串,这时候非贪婪模式就可以派上用场了
let ret = /<div>.*?<\/div>/.exec(html) // "<div>div1</div>"
再举个例子,比如要截取一个图片"a.jpg.jpg.png"
字符串的"a.jpg"
let url = "a.jpg.jpg.png";
let ret = /.*?\.jpg/.exec(url) // a.jpg
字符串和正则
字符串有4个方法可以使用正则,分别是match()
,replace()
,search()
,split()
String.replace
String.replace()
可以使用正则来替换字符串,例如
let str = "abc you abc me";
str.replace(/abc/g, 'fuck'); // fuck your fuck me
第二个参数还可以是回调函数,于是上面的写法还可以这样写
let str = "abc you abc me";
str.replace(/abc/g, match => {
// match是正则匹配到的字符串
return 'fuck';
})
String.replace和while循环
例如我们想要获取url的所有字符串,我们可能会这么写
let url = "ttp://a.com?a=1&b=abc&q=%e5%b0%bc%e7%8e%9b&名字=xx";
let reg = /[\?&]([^&#]+)=([^&#]*)/g, params = {}, ret;
// 使用while
while (ret = reg.exec(url)) {
params[ret[1]] = ret[2];
}
console.log(params);
[^&#] 表示除了&和#外的所有字符
String.replace()
在全局模式下,第二个参数回调会一直调用知道搜索结束,所以可以这样写
let url = "ttp://a.com?a=1&b=abc&q=%e5%b0%bc%e7%8e%9b&名字=xx";
let reg = /[\?&]([^&#]+)=([^&#]*)/g, params = {}, ret;
// 使用replace
url.replace(reg, (match, sub1, sub2) => {
params[sub1] = sub2;
})
console.log(params);
正向肯定预查
(?=pattern)
是正向肯定预查,例如:Windows(?=98|2000) 可以匹配Windows2000的Windows,不可以匹配Windows2008的Windows
利用这个规则可以实现匹配大小写和数字的正则
let reg = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[\d]).{6,12}$/
看上去有点难懂,其实可以这样记,?=.*
可以记为至少包含一个,比如
(?=.*[a-z])
至少有一个小写字母(?=.*foo)
至少有一个foo
(x)和(?:x)的区别
两个都是表示匹配x,(x)是记住匹配项,(?:x)不记住匹配项,其实就是匹配的结果要不要留给后续的操作使用
/(foo) (bar) \1 \2/.exec('foo bar foo bar') // ["foo bar foo bar", "foo", "bar"],\1 \2 类似于字符串替换时候使用的 $1 $2
/(foo) (bar) \1 \2/.exec('foo bar foo foo') // null
/(?:foo) (bar)/.exec('foo bar hello world') // ['foo bar', 'bar']
/(foo) (bar)/.exec('foo bar hello world') // ['foo bar', 'foo', 'bar']