# 正则
RegExp 对象表示正则表达式,它是对字符串执行模式匹配的强大工具.
+ replace match split search
+ test exec compile
+ ? ?= ?! (?:) () $1
# 正则es6变化
ES5 中,如果使用构造函数 RegExp()初始化 RegExp 对象(即正则表达式),并且它的第一个参数是正则表达式时,那么不允许再传入标志字符串(即第二个参数)。而 ES6 更改了这项限制,如果传入了第二个参数,那么就会取代第一个参数中的标志,如下所示。
var reg = new RegExp(/\w/g, "i");
reg.toString(); //"/\w/i"
- u 标志:由于正则表达式无法正确处理辅助平面中的 Unicode 字符,因此 ES6 新增了u 标志,使其能够处理两个编码单元的字符。依然以字符“ ”为例,下面是两条正则表达式的匹配语句,返回的结果都是 false。
var word = " ";
/^.$/.test(word); //false
/\u{20BB3}/.test(word); //false
第一条匹配语句中的元字符“.”表示除换行符之外的任意字符,但只能匹配一个字符,而“𠮳”却会被当成两个字符来对待,因此匹配失败;第二条匹配语句中使用了 Unicode 转义字符的新形式,但正则表达式会将花括号中的内容识别成量词,因此匹配也失败。下面的代码会为两个正则表达式添加 u 标志,此时就能成功匹配。
/^.$/u.test(word); //true
/\u{20BB3}/u.test(word); //true
ES6 还为正则表达式增加了一个只读的布尔属性:Unicode,指示正则表达式是否携带了u 标志。
- y 标志:此标志也叫黏性(sticky)标志,当正则表达式中携带 y 标志时,匹配会从
lastIndex属性指定的位置开始。与 g 标志不同,如果在起始处的第一个位置没有匹配成功,那么就会终止匹配,下面演示了这两个标志的区别。
var str = "pw1st2pw3",
pattern1 = /pw\d/g,
pattern2 = /pw\d/y;
console.warn(pattern1.lastIndex) //0
console.warn(pattern2.lastIndex) //0
console.warn(pattern1.test(str), 1)//true
console.warn(pattern2.test(str), 2)//true
console.warn(pattern1.lastIndex) //3
console.warn(pattern2.lastIndex) //3
console.warn(pattern1.test(str), 3)//true
console.warn(pattern2.test(str), 4)//false
示例中的两个正则表达式分别携带 g 和 y 标志,用于匹配“pw”后跟一个数字,各自调用了两次 test()方法,在第二次调用时都会更新 lastIndex 属性的值,此时得到了两个不同的结果。
ES6 也为正则表达式增加了一个用于标识是否携带 y 标志的布尔属性:sticky,与 unicode属性一样也是只读的。
- flags 属性:在 ES5 的时代,只能通过正则表达式的 source 属性得到模式规则,而 ES6新增的 flags 属性还可以获取到它所携带的标志,但要注意,返回的值得按“gimuy”的顺序排列。这两个都是只读属性,具体使用如下所示。
var pattern = /pw\d/img;
pattern.source; //"pw\d"
pattern.flags; //"gim"
# 方括号
方括号用于查找某个范围内的字符
# RegExp 对象属性
| 属性 | 描述 |
|---|---|
| global | RegExp 对象是否具有标志 g。 |
| ignoreCase | RegExp 对象是否具有标志 i。 |
| lastIndex | 一个整数,标示开始下一次匹配的字符位置。 |
| multiline | RegExp 对象是否具有标志 m。 |
| source | 正则表达式的源文本。 |
var str = "Visit W3School.com.cn";
var patt1 = new RegExp("s","g");
if(patt1.global)
{
document.write("Global property is set");
}
else
{
document.write("Global property is NOT set.");
}
//Global property is set
- new RegExp(x,"g");x也可以是正则,然后修改他的配置
# 注意转义的元字符
如果需要使用它本义,则需要转义
([{\^$|}])?*+.
/\.at/gi
# 支持正则表达式的 String 对象的方法
| 方法 | 描述 |
|---|---|
| search | 检索与正则表达式相匹配的值。 |
| match | 找到一个或多个正则表达式的匹配。 |
| replace | 替换与正则表达式匹配的子串。 |
| split | 把字符串分割为字符串数组。 |
string.split(separator,number)- 参数number(≥0)用于指定数组的大小,以便确保返回的数组不会超过既定大小。
var colorText = "red,blue,green,yellow";
var colors3 = colorText.split(/[^\,]+/); //["", ",", ",", ",", ""]
//在本例中就是以red,blue,green,yellow作为分隔符,
//因为red作为分隔符前面没有内容,所以是空字符串,最后一个空字符串也是这样的道理。
# search()
返回值
stringObject 中第一个与 regexp 相匹配的子串的起始位置。
注释:如果没有找到任何匹配的子串,则返回 -1。
search() 方法不执行全局匹配,它将忽略标志 g。它同时忽略 regexp 的 lastIndex 属性,并且总是从字符串的开始进行检索,这意味着它总是返回 stringObject 的第一个匹配的位置。
var str="Hello world! world"
document.write(str.search(/world/g) + "<br />")
document.write(str.search("World") + "<br />")
//6
//-1
# $1$2$3...
多少个小括号,多少个$n
//创建一个需要匹配的字符串
var objStr = "这是我的手机号码13182345678, 这是我朋友的手机号码13912345678";
//创建匹配手机号码的正则表达式对象,隐式创建
var reg = /(13)(\d{2})(\d{7})/g; //g为全局匹配参数,匹配11位手机号码;
//将字符串进行匹配如果符合结果则将返回结果放入数组中
var arr = objStr.match(reg);
with(document) {
if (arr != null) { //如果匹配有结果
write("检测到" + arr.length + "个手机号码"); //显示匹配到多少个电话号码
//循环输出各个结果,并且判断运营商
for (var i = 0; i < arr.length; i++) {
//对匹配出来的电话号码进行二次匹配,获取号码的第三个数字
arr[i] = arr[i].toString().replace(reg, "$1**$3");
//以列表的形式输出各个电话号码
write("<li>" + arr[i]);
//如果第三个电话号码小于等于3则该电话号码为联通号码,否则为其他运营商的号码
if (RegExp.$2 <= 33) {
write("这是一个联通手机号码!");
} else {
write("这是一个移动或者电信手机号码!");
}
} //for循环的结束
} //if的结束
} //with的结束
//检测到2个手机号码
// 13**2345678这是一个联通手机号码!
// 13**2345678这是一个移动或者电信手机号码!
注意:/(13)(\d){2}(\d{7})/g 如果这样会出现$2实际上查的是第四个数的值,本以为会分成四个$段,结果依然是三个,而且(\d){2}所要接受的$2只有一位了!!!
# JS中match() 和 exec() 返回值和属性
exec() 是RegExp对象的方法,而 match() 是String对象的方法。 都会返回包含第一个匹配项信息的数组;或者在没有匹配项的情况下返回null。 返回的数组虽然是Array 的实例,但包含两个额外的属性:index 和 input。其中,index 表示匹配项在字符串中的位置,而 input 表示应用正则表达式的字符串。 在数组中,第一项是与整个模式匹配的字符串,其他项是与模式中的捕获组匹配的字符串(如果模式中没有捕获组,则该数组只包含一项)。
var text = "mom and dad and baby";
var pattern = /(mom and )?(dad and )?baby/;
var matches = text.match(pattern);//pattern.exec(text);
console.log(matches)
console.log(matches.index);
console.log(matches.input);
console.log(matches[0]);
console.log(matches[1]);
console.log(matches[2]);
//["mom and dad and baby", "mom and ", "dad and ", index: 0,
//input: "mom and dad and baby", groups: undefined]
// 0
// mom and dad and baby
// mom and dad and baby
// mom and
// dad and
//const arr= ['mom and dad and baby', ' and dad and baby', ' and baby', index: 0, input: 'mom and dad and baby', groups: undefined]
// 报错Uncaught SyntaxError: Unexpected token ':'
var text = "mom and dad and baby";
var pattern = /(mom and )?(dad and )?baby/;
var matches = pattern.exec(text);//text.match(pattern);
console.log(matches);
console.log(matches.index);
console.log(matches.input);
console.log(matches[0]);
console.log(matches[1]);
console.log(matches[2]);
// ['mom and dad and baby', 'mom and ', 'dad and ', index: 0, input: 'mom and dad and baby', groups: undefined]
// 0
// mom and dad and baby
// mom and dad and baby
// mom and
// dad and
- match是字符串使用的方法,而exec是正则的方法,match加g和不加g使用结果:参见
- exec:如果设置了全局标记,每次返回一个匹配信息,如果没有设置,每次都返回第一个信息
let text = "cat, bat, sat, fat";
let pattern = /.at/;
let matches = pattern.exec(text);
console.log(matches.index); // 0
console.log(matches[0]); // cat
console.log(pattern.lastIndex); // 0
matches = pattern.exec(text);
console.log(matches.index); // 0
console.log(matches[0]); // cat
console.log(pattern.lastIndex); // 0
let text = "cat, bat, sat, fat";
let pattern = /.at/g;
let matches = pattern.exec(text);
console.log(matches.index); // 0
console.log(matches[0]); // cat
console.log(pattern.lastIndex); // 3
matches = pattern.exec(text);
console.log(matches.index); // 5
console.log(matches[0]); // bat
console.log(pattern.lastIndex); // 8
matches = pattern.exec(text);
console.log(matches.index); // 10
console.log(matches[0]); // sat
console.log(pattern.lastIndex); // 13
pattern.lastIndex:每次调用exec(),会更新lastIndex;如果无法返回的时候会报错! 【Uncaught TypeError: Cannot read properties of null (reading 'index')】
- 粘连标记y,在exec会覆盖g,每次调用只会寻找lastIndex位置上寻找匹配项
let text = "cat, bat, sat, fat";
let pattern = /.at/y;
let matches = pattern.exec(text);
console.log(matches.index); // 0
console.log(matches[0]); // cat
console.log(pattern.lastIndex); // 3
// There is no match starting at character index 3, so exec() will return null
// exec() finding no matches resets lastIndex to 0
matches = pattern.exec(text);
console.log(matches); // null
console.log(pattern.lastIndex); // 0
// Advancing lastIndex will allow a sticky regex exec() to find the next match:
pattern.lastIndex = 5;
matches = pattern.exec(text);
console.log(matches.index); // 5
console.log(matches[0]); // bat
console.log(pattern.lastIndex); // 8
# 捕获性分组和非捕获性分组
捕获性分组:() 捕获性分组工作模式()会把每个分组里匹配的值保存起来。
比如利用捕获性分组把 hello world 互换成 world hello:
var str = 'hello world';
var pattern = /([a-z]+)\s([a-z]+)/;
var n_str = str.replace(pattern,"$2 $1"); //这里的$1、$2与方法二里的RegExp.$1、RegExp.$2作用是相同的。
console.log(n_str) //world hello
上面的match和exec等也可以利用捕获性分组去实现某些功能
非捕获性分组工作模式下分组(?:)会作为匹配校验,并出现在匹配结果字符里面,但不作为子匹配返回。
// 比如利用非捕获性分组获取字符串000aaa111,而且只返回一个值为aaa111的数组:
// 先看用捕获性分组匹配会返回什么
var str1 = '000aaa111';
var pattern = /([a-z]+)(\d+)/; //捕获性分组匹配
var arr = pattern.exec(str1);
console.log(arr) //['aaa111','aaa','111'] 结果子串也获取到了,这并不是我们想要的结果
//非捕获性分组
var str2 = '000aaa111';
var pattern2 = /(?:[a-z]+)(?:\d+)/; //非捕获性分组匹配
var arr2 = pattern2.exec(str2);
console.log(arr2) //['aaa111']
# ()\1反向引用
捕获组捕获到的内容在正则表达式内部进行引用,则为反向引用
通过编号引用: \number, 如 \1, \2
通过命名引用: \k\
/(ab)\1/ // 匹配'abab', \1表示(ab)匹配到的内容
/(ab)\1{2}/ // 匹配'ababab', \1表示(ab)匹配到的内容
语法
元字符 \1~\9 作用:用来表示前面以匹配的字符或分组的一个引用
//一般情况,当我们想匹配任意两个相同的字符(复杂一点就是两个相同的分组)时,往往可以借助下面的写法
//说明:
//(\w)用来匹配任何除了换行和制表符的字符, 而\1是对(\w)的一个引用, 所以你可以理解成: (\w)\1 就是(\w)(\w)
//但是,
//(\w)\1 和 (\w)(\w)的不同之处在于, (\w)(\w)表示任意两个连续的字符, 比如Ac, MM, K9, 都可以,
// 但(\w)\1只能是AA, CC, 99 这样连续相同的字符
//所以, 你可以这样理解, \1 是对(\w)的一个实例化引用, 当(\w) 匹配到A时, \1 被表达成A, 当(\w)匹配9时, \1 被表示成9
//说了这么多, 可能有些废话, 下面这个例子就很好理解了
var str = "AA Am 99";
var reg = /(\w)\1/g;
str.match(reg);//输出: ["AA", "99"]
# 正则表达式应用场景
- 对输入内容校验是否正确
var mail =/^.+@mail.com|cn$/
var a=mail.test("...@mail.cn")
console.log(a)
- 和谐或者保护某些内容
var a="护卫护卫邱娥国爱仕达多所护卫我的企鹅厚度"
var b= a.replace(/护卫/gi,"**")
console.log(b)//****邱娥国爱仕达多所**我的企鹅厚度
- 截取有效信息
var reg=new RegExp("(http://www.qidian.com/BookReader/)(\\d+),(\\d+).aspx","gmi");
var url="http://www.qidian.com/BookReader/1017141,20361055.aspx";
var rep=url.replace(reg,"$2");
console.log(rep)//1017141
var obj="13172345678";
var reg = /(13)(\d){2}(\d{7})/g;
var k = obj.replace(reg, "$1**$3");
var obj1="13392345678";
var reg1 = /(13)(\d){2}(\d{7})/g;
var k1 = obj1.replace(reg1, "$1**$3");
console.log(RegExp.$2)//9取到最近的那个正则表达式且参与实际应用的$2
和谐某些字但是同时不影响用到这个字的正常词语,可以使用量词?=n ?!n
- ?=n 匹配任何其后紧接指定字符串 n 的字符串。
- ?!n 匹配任何其后没有紧接指定字符串 n 的字符串。
- 前瞻分为正向前瞻和反(负)向前瞻,正向前瞻(?=表达式)表示后面要有什么,反向前瞻(?!表达式)表示后面不能有什么。
var str="Is is this all there is xxx all";
var patt1=/is(?! all)/g;
document.write(str.replace(patt1,1));
// Is 1 this all there 1 xxx all
var str="Is is this all there is xxx all";
var patt1=/is(?= all)/g;
document.write(str.replace(patt1,1));
//Is is th1 all there is xxx all
- 统计出现次数(match)[要加g]+获取需要的值(加上小括号的特定值,然后可以根据数组第二位开始依次对号入座)
var str="1 plus 2 equal 3"
console.log(str.match(/\d+/))
//["1", index: 0, input: "1 plus 2 equal 3", groups: undefined]
var str="1 plus 2 equal 3"
console.log(str.match(/\d+/g))
//["1", "2", "3"]
var str2="1 plus2 equal 3"
console.log(str2.match(/1 plus(\d?) equ(\w+) (\d?)/))
["1 plus2 equal 3", "2", "al", "3", index: 0, input: "1 plus2 equal 3", groups: undefined]
# ?限定符
匹配前面的子表达式零次或一次,0或1,可以使用{0,1}代替
在平常webpack的rules配置中,用来匹配ts和tsx文件
{
test: /\.tsx?$/
}
# 非贪婪匹配
能匹配少的就不匹配多的原则
const str = 'sd2345s423987sd2342'
console.log(str.match(/\d{3,5}?/g))
// 因为增加了"?"符合,导致只要有符合3个连续数字的就返回。
// ["234", "423", "987", "234"]
console.log(str.match(/\d{3,5}/g))
// 去掉"?"后,变成了默认模式,就是贪婪匹配
// ["2345", "42398", "2342"]
const str = '<img class="latex" style="width:2.323ex; height:2.009ex; vertical-align: -0.171ex;" src="https://td.com/1.png" /><span>s</span>'
console.log(str.match(/<img.*?\/?>/g))
// ["<img class="latex" style="width:2.323ex; height:2.…l-align: -0.171ex;" src="https://td.com/1.png" />"]
// 下面把第一个"?"去掉,发现结果就是全部匹配了。
console.log(str.match(/<img.*\/?>/g))
// ["<img class="latex" style="width:2.323ex; height:2.…1ex;" src="https://td.com/1.png" /><span>s</span>"]
# 先行断言 和 先行否定断言
x 只有在 y 前面才匹配,必须写成/ x(?=y)/的形式
举例:查找字符串%前面的数字
const str = 'The best way to achieve a goal is to devote 100% of your time and energy to it.'
console.log(str.match(/\d+%/g))//['100%']
console.log(str.match(/\d+(?=%)/g))//['100']
x 只有不在 y 前面才匹 配,必须写成/ x(?!y)/的形
举例:查找字符串中数字不在%前面的数字
let str = 'I have more than 100 books'
console.log(str.match(/\d+(?!%)/g))
// ["100"]
str = 'I have more than 100 books, but only 20% is about software'
console.log(str.match(/\d+(?!%)/g))
// 【注意】2也匹配到了,如果需要把20也过滤掉,需要额外加代码
// ["100", "2"]
# 非获取匹配
/(?:js|golang) is good/.exec('js is good, golang is good')
// js这个词虽然有()进行处理,但是不会被匹配到,而是整体输出`js is good`
// ["js is good", index: 0, input: "js is good, golang is good", groups: undefined]
/(js|golang) is good/.exec('js is good, golang is good')
// 去掉“?:”之后的结果
// ["js is good", "js", index: 0, input: "js is good, golang is good", groups: undefined]
# 后行断言 和 后行否定断言
"后行断言"正好与"先行断言"相反 , x 只有在 y 后面才匹配 , 必须写成/(?<=y)x/的 形式
let str = `I spent ¥100 RMB to buy this book`
console.log(/(?<=¥)\d+/.exec(str))//['100', index: 9, input: 'I spent ¥100 RMB to buy this book', groups: undefined]
x 只有不在 y 后面才匹配,必须写成/(?<! y)x/的形式
举例:查找不是¥后面的数字
let str = `I spent $100 RMB to buy this book`;
console.log(/(?<!¥)\d+/.exec(str))
// ["100", index: 9, input: "I spent $100 RMB to buy this book", groups: undefined]