JavaScript 正则 下
一、量词
量词很简单是代表数量的词
量词 | 描述 |
---|---|
n+ | 匹配任何包含至少一个 n 的字符串 |
n* | 匹配任何包含零个或多个 n 的字符串 |
n? | 匹配任何包含零个或一个 n 的字符串 |
n{X} | 匹配包含 X 个 n 的序列的字符串 |
n{X,Y} | 匹配包含 X 至 Y 个 n 的序列的字符串 |
n{X,} | 匹配包含至少 X 个 n 的序列的字符串 |
n$ | 匹配任何结尾为 n 的字符串 |
^n | 匹配任何开头为 n 的字符串 |
?=n | 匹配任何其后紧接指定字符串 n 的字符串 |
?!n | 匹配任何其后没有紧接指定字符串 n 的字符串 |
1、量词就是规定字符重复的次数
n+
n 是个变量,可以代表任何东西,
+ 加号代表这个变量 n 可以重复出现一次到无数次
也就是说 n 现在的区间是 {1, Infinity} 1到正无穷,Infinity 正无穷就不写了,写一个逗号这样 {1, }
/\w+/
加号的意思是前面这个变量\w,可以出现一次到多次,也就是说这个 \w 可以重复出现无数次(多少次都可以)
下面的正则表达式能不能匹配成功?可以匹配成功返回 ["abc"],相当于 \w 出现了三次
var reg = /\w+/g; var str = "abc"; console.log(str.match(reg)); // ["abc"]
n*
区间是0到正无穷 {0, Infinity}
/\w*/
星号意思是可以出现 0 次到多次,下面能匹配出来吗?肯定能匹配出来返回 ["abc", ""]
var reg = /\w*/g; var str = "abc"; console.log(str.match(reg));// ["abc", ""]
匹配出两个 ["abc", ""] ,一个是 abc,后面还加了一个 "" 空串,匹配出来一个空是什么意思?
1. 它匹配的时候非常智能,先看 "abc" 符合要求,符合要求算一个
2. 然后还有修饰符 g 意思是全局匹配,现在光标在 c 后面,c 后面还有一段距离叫逻辑上的距离,什么是逻辑上的距离?
3. /\w*/ 如果 * 变成 0 的话,那匹配就是空了,然后在 c 后面的逻辑点上又往后匹配出一个空出来
4. 一开始 * 变成 abc,第二次再匹配 * 变成零了,换句话说正则表达式匹配出一个空出来
再看 /\d*/ 能匹配出字符串 "abc" 的结果吗?能,匹配出四个空 ["", "", "", ""]
var reg = /\d*/g; var str = "abc"; console.log(str.match(reg));// ["", "", "", ""]
匹配出四个结果都是空,那是逻辑上光标依次往右去移动,
1. 第一次光标在第一个 a 前面的时候是 0,所以把 a 前面匹配出来了,
2. a 匹配不了,然后到 a 后面去,a 后面还是一个0,在乘个 0 还是一个空,所以有多少个光标定位点就有多少个空
上面的 /\w*/ 怎么没匹配那么多空呢?
一旦 \w 能识别出值,它会先看 a 能识别、b 能识别、c 能识别,它会先一连串的把这些能识别的识别了,到最后的时候才试一下乘 0,原则是能匹配多就不匹配少。
比如下面字符串是一排 "aaaaaaaaaa",正则 /\w+/g 能把这一排 a 全匹配出来返回 ["aaaaaaaaaa"]
var reg = /\w+/g; var str = "aaaaaaaaaa"; console.log(str.match(reg)); // ["aaaaaaaaaa"]
为什么不一个一个匹配?
因为正则的原则就是能多就不少,正则表达式有个原则叫“贪婪匹配原则”能多就不少(能四个尽量就不三个)
n?
问号也是一个量词,能取的值 0 到 1 个 {0, 1}
下面的匹配的是多少个?能把所有的 a 都取出来 ["a", "a", "a", "a", "a", "a", "a", "a", "a", "a", ""] 但最后还要加一个空串,因为到最后的时候,后面没有 a 了加一个空。
var reg = /\w?/g; var str = "aaaaaaaaaa"; console.log(str.match(reg));// ["a", "a", "a", "a", "a", "a", "a", "a", "a", "a", ""]
n{x}
意思是 x 个,把这个区间定死了
/\w{3}/g 填3就是3个,3个的匹配返回 ["aaa", "aaa", "aaa"]
var reg = /\w{3}/g; var str = "aaaaaaaaaa"; // 字符串有10个a console.log(str.match(reg));// ["aaa", "aaa", "aaa"]
n{x, y}
还可以填区间意思是x到y个
/\w{3,5}/g 3个到5个
1. 首先符合匹配原则,能5个就别3个4个,实在5个不了了,再试试3个行不行
2. 字符串有18个a,匹配结果 ["aaaaa", "aaaaa", "aaaaa", "aaa"] 前面三组5个a最后一组3个a
var reg = /\w{3,5}/g; var str = "aaaaaaaaaaaaaaaaaa"; // 字符串有18个a console.log(str.match(reg)); // ["aaaaa", "aaaaa", "aaaaa", "aaa"]
如果字符串是19个a呢?还是贪婪匹配原则能四个就不三个
var reg = /\w{3,5}/g; var str = "aaaaaaaaaaaaaaaaaaa"; console.log(str.match(reg)); //["aaaaa", "aaaaa", "aaaaa", "aaaa"]
这个 n{X, Y} 区间还可以填的更奔放一点,后面的 y 可以不写,不写的意思是 Ifinity,x到正无穷 {x, Ifinity},
可以自定义1到正无穷 {1, },1到正无穷就是1到不写,1到不写就和+号没区别
1到不写 和 +号,一样全给匹配出来
var str = "aaaaaaaaaaaaaaaaaaa"; var reg = /\w{1,}/g; // 1到正无穷{1, } console.log(str.match(reg));//["aaaaaaaaaaaaaaaaaaa"] var reg = /\w+/g; // "+号"1到多个 console.log(str.match(reg));//["aaaaaaaaaaaaaaaaaaa"]
{1, } 1到不写和 + 号没区别
{0, } 0到不写和 * 号没区别
{2, } 2到正无穷和3到正无穷是我们可以定义的
量词很简单,但是记住量词它“量”的是什么?
n 乘以这个量词,也就是 \w 乘以这个量词,不是 \w 的结果乘以这个量词,一定得记住。
如果 \w的结果 乘以这个量词,那匹配出来的东西必须都是一致的,要是a后面全得跟着a,ab都不行。
意思是字符串"abcde",通过 /\w{2,}/g 能匹配出来吗?能匹配出来,量词“量”的是\w,是 \w 乘很多东西,然后每一位 \w 在随机匹配,而不是 \w 先匹配一个结果再乘量词
var reg = /\w{2,}/g; var str = "abcde"; console.log(str.match(reg)); // ["abcded"]
通过量词匹配,保证不了每一位都相同,只能保证每位都取自同一个区间
var reg = /a{1,2}/g; var str = "aaaaabcde"; console.log(str.match(reg));//["aa", "aa", "a"] var reg = /a{2,}/g; var str = "aaaaabcde"; console.log(str.match(reg));//["aaaaa"] var reg = /a{2,}/g; var str = "aa"; console.log(str.match(reg)); // ["aa"] var reg = /a{2,}/g; var str = "a"; console.log(str.match(reg)); // null var reg = /a{1,}/g; var str = "aaaa"; console.log(str.match(reg)); // ["aaaa"] var reg = /a{2,3}/g; var str = "aaaaaaaaaa"; console.log(str.match(reg)); // ["aaa", "aaa", "aaa"] 十个a只匹配出三组九个 var reg = /a{1,3}/g; var str = "aaaaaaaaaa"; console.log(str.match(reg)); // ["aaa", "aaa", "aaa", "a"] 十个a匹配出三组零一个
2、下面 n$ 和 ^n,一个匹配结束,一个匹配开头跟量词没关系,不能算是量词里面的
^n
匹配任何开头为 n 的字符串
正则 /abc/g 能匹配字符串 "abcded" 出来吗?能匹配出来,字符串里面有 abc 的片段
var reg = /abc/g; var str = "abcded"; console.log(str.match(reg)); // ["abc"]
/^abc/g
现在有一个要求匹配以 abc 开头的,可以这么理解,开头的 a 后面必须跟着 bc,
这样的 abc 能匹配出来吗?也可以匹配出
var reg = /^abc/g; var str = "abcded"; console.log(str.match(reg)); // ["abc"]
n$
匹配任何结尾为 n 的字符串
/abcd$/g 以 abcd 结尾能匹配出来吗?匹配不出来了
var reg = /abcd$/g; var str = "abcded"; console.log(str.match(reg)); // ["null"]
/ed$/g 以 ed 结尾呢?能匹配出来返回["ed"]
var reg = /ed$/g; var str = "abcded"; console.log(str.match(reg)); // ["ed"]
符号 $ 写在后面,就是以什么什么结尾,符号 $ 会受修饰符 m 的影响,可以多行匹配
3、问一个问题
下面以 abc 开头,以 abc 结尾能匹配成功吗?
var reg = /^abc$/g; var str = "abcadc"; console.log(str.match(reg)); // ["null"]
不能匹配成功,
这么理解以当前 abc 开头,还得以当前这个 abc 结尾,必须开头结尾是同一套,
这样一个开头、一个结尾就能起到一个作用,把这个字符串限定死,这个字符串必须得是 abc,
一个开头一个结尾能达到限定字符串的作用,限定字符串的长度,有开头有结尾就把字符串固定住了。
只有字符串 "abc" 是最搭配的
var reg = /^abc$/g; var str = "abc"; console.log(str.match(reg)); // ["abc"]
4、正则面试题
写一个正则表达式,检验字符串首尾是否含有数字?
这题的重点不在写,重点在于能否读懂,首尾是否含有数字什么意思?
是首有、尾也得有,还是首有就行或者尾有就行?
首尾是否含有数字?
中华文化博大精深,“首尾是否含有”没有说首尾都含有,没有说都字,所以首尾含所有数字的意思是首有就行,尾有也行
var reg = /^\d|\d$/g; // 开头是数字或结尾是数字 var str = "123abc"; console.log(str.match(reg)); // ["1"] console.log(reg.test(str)); // true
如果首或尾“都”含有数字怎么写?
var reg = /^\d[\s\S]*\d$/g; // 小s大S代表任何东西,*号出现0到多个,中间拿一个区间拉伸 var str = "123abc456"; console.log(str.match(reg)); // ["123abc456"] console.log(reg.test(str)); // true
之前学PHP时候说 (.*) 是一个经典的用法,匹配所有的任何内容0次1次多次
var reg = /^\d(.*)\d$/g; var str = "123abc456"; console.log(str.match(reg)); // ["123abc456"] console.log(reg.test(str));// true
二、RegExp对象的属性
属性什么意思呢?
正则表达式是一个对象,对象上势必会有些属性的
属性 | 描述 | FF | IE |
---|---|---|---|
global | RegExp 对象是否具有标志 g | 1 | 4 |
ignoreCase | RegExp 对象是否具有标志 i | 1 | 4 |
lastIndex | 一个整数,标示开始下一次匹配的字符位置 | 1 | 4 |
multiline | RegExp 对象是否具有标志 m | 1 | 4 |
source | 正则表达式的源文本 | 1 | 4 |
ignoreCase
reg.ignoreCase 返回 false,证明上面没写修饰符 i,如果写了 i 就返回 true
var reg = /^\d[\s\S]*\d$/g; console.log(reg.ignoreCase); // false
global
现在写了修饰符 g,reg.global 返回 ture
var reg = /^\d[\s\S]*\d$/g; console.log(reg.global); // ture
multiline(多行)
没有写修饰符 m 多行,reg.multiline 返回 false
var reg = /^\d[\s\S]*\d$/g; console.log(reg.multiline); // false
source
该属性代表里正则表达式里面的体,写的内容能给显示出来,用字符串来展示出来
var reg = /^\d[\s\S]*\d$/g; console.log(reg.source); // ^\d[\s\S]*\d$
以上这些属性都无关紧要,有一个属性是最有用的就是 lastIndex,这个属性要到下面 exec 方法时候再说
三、RegExp对象的方法
方法 | 描述 | FF | IE |
---|---|---|---|
compile | 编译正则表达式。 | 1 | 4 |
exec | 检索字符串中指定的值。返回找到的值,并确定其位置。 | 1 | 4 |
test | 检索字符串中指定的值。返回 true 或 false。 | 1 | 4 |
正则表达式的 test() 方法里面填一个字符串,来检验能不能符合要求,符合要求返回 true 不符合要求 false,但是功能太局限了
有一个 NB 的方法 exec()(读“以刻载课”,执行的意思)也是一个匹配方法,这个方法是本篇重点。
exec() 方法非常有意思,下面写一个例子
var reg = /ab/g; var str = "abababab"; // 字符串有四个"ab" console.log(reg.exec(str)); // ["ab", index: 0, input: "abababab", groups: undefined] console.log(reg.exec(str)); // ["ab", index: 2, input: "abababab", groups: undefined] console.log(reg.exec(str)); // ["ab", index: 4, input: "abababab", groups: undefined] console.log(reg.exec(str)); // ["ab", index: 6, input: "abababab", groups: undefined] console.log(reg.exec(str)); // null console.log(reg.exec(str)); // ["ab", index: 0, input: "abababab", groups: undefined]
执行四次 reg.exec(str) 匹配成功,第五次执行输出 null,再执行它依然这样的循环
第一次执行返回的信息
var reg = /ab/g; var str = "abababab"; console.log(reg.exec(str));// ["ab", index: 0, input: "abababab", groups: undefined]
这是返回的信息 ["ab", index: 0, input: "abababab", groups: undefined]
第一个是 "ab" 是匹配的一个体,index 是 0,返回的是类数组
index 是索引的意思,也就是说这个游标在字符串的哪个位置匹配的,ab 在第 0 个位置(abababab)所以 index 是 0
下一次再执行 reg.exec(str)
正则有 g 修饰符号,注意有 g 和没 g 不一样,有 g 修饰符再执行一次,结果是 index: 2 变了,2是"abababab"匹配的是红色 ab 的位置是2,第一次匹配的是第一组ab,第二次是在第一次的基础上接着匹配的
var reg = /ab/g; var str = "abababab"; console.log(reg.exec(str));// ["ab", index: 0, input: "abababab", groups: undefined] console.log(reg.exec(str));// ["ab", index: 2, input: "abababab", groups: undefined]
再执行两次,
index 的值分别是 0、2、4、6,字符串的游标 "0ab2ab4ab6ab"
var reg = /ab/g; var str = "abababab"; console.log(reg.exec(str));// ["ab", index: 0, input: "abababab", groups: undefined] console.log(reg.exec(str));// ["ab", index: 2, input: "abababab", groups: undefined] console.log(reg.exec(str));// ["ab", index: 4, input: "abababab", groups: undefined] console.log(reg.exec(str));// ["ab", index: 6, input: "abababab", groups: undefined]
第四次匹配之后,游标到 "abababab游标位置" 红色文字这里,再匹配一次能匹配到吗?再匹配一边之后发现打印的是 null
var reg = /ab/g; var str = "abababab"; console.log(reg.exec(str));// ["ab", index: 0, input: "abababab", groups: undefined] console.log(reg.exec(str));// ["ab", index: 2, input: "abababab", groups: undefined] console.log(reg.exec(str));// ["ab", index: 4, input: "abababab", groups: undefined] console.log(reg.exec(str));// ["ab", index: 6, input: "abababab", groups: undefined] console.log(reg.exec(str));// null
如果再匹配呢?
返回到第一次匹配的了,就好像这个游标在一圈一圈的轮圈的转
var reg = /ab/g; var str = "abababab"; console.log(reg.exec(str));// ["ab", index: 0, input: "abababab", groups: undefined] console.log(reg.exec(str));// ["ab", index: 2, input: "abababab", groups: undefined] console.log(reg.exec(str));// ["ab", index: 4, input: "abababab", groups: undefined] console.log(reg.exec(str));// ["ab", index: 6, input: "abababab", groups: undefined] console.log(reg.exec(str));// null console.log(reg.exec(str));// ["ab", index: 0, input: "abababab", groups: undefined] console.log(reg.exec(str));// ["ab", index: 2, input: "abababab", groups: undefined] console.log(reg.exec(str));// ["ab", index: 4, input: "abababab", groups: undefined] console.log(reg.exec(str));// ["ab", index: 6, input: "abababab", groups: undefined] console.log(reg.exec(str));// null
而这个游标到底有没有呢?
有,这个游标的属性就是 reg.lastIndex
这个 lastIndex 和 exec 方法是相协调匹配的,lastIndex 就是为了 exec 方法而存在的,它就是游标
var reg = /ab/g; var str = "abababab"; // 第一次匹配 console.log(reg.lastIndex);// 0 console.log(reg.exec(str));// ["ab", index: 0, input: "abababab", groups: undefined] // 第二次匹配 console.log(reg.lastIndex);// 2 console.log(reg.exec(str));// ["ab", index: 2, input: "abababab", groups: undefined] // 第三次匹配 console.log(reg.lastIndex);// 4 console.log(reg.exec(str));// ["ab", index: 4, input: "abababab", groups: undefined] // 第四次匹配 console.log(reg.lastIndex);// 6 console.log(reg.exec(str));// ["ab", index: 6, input: "abababab", groups: undefined]
exec 方法通过找游标的位置,来进行下一次匹配,
第一次游标的位置是 0,然后会在游标的位置向后匹配,匹配到下一个为止,它会把 lastIndex 这个游标放到第二位上,再匹配会在 lastIndex 的基础上,去往后再匹配依次去变,
游标的变化是 lastIndex 的变化,每匹配一次后结束的位置就是下一次 lastIndex 开始的位置,最后匹配一圈后又归零了。
如果每次把 lastIndex 手动修改一下呢?
var reg = /ab/g; var str = "abababab"; console.log(reg.exec(str));// ["ab", index: 0, input: "abababab", groups: undefined] console.log(reg.lastIndex); // 1.第一次匹配完后“lastIndex”应该是2 reg.lastIndex = 0; // 2.现在lastIndex再变回0 console.log(reg.exec(str)); // 3. 再匹配一次index还是0 ["ab", index: 0, input: "abababab", groups: undefined] console.log(reg.lastIndex); // 现在再匹配lastIndex是2,它还是正常往后移动的
手动把游标给挪回 0 之后,下一次 index 还是从第 0 位匹配,从哪里开始匹配是完全由 lastIndex 来控制
如果不加全局修饰符 g 会发生什么样的变化呢?发现 lastIndex 根本就不移动,永远从第一位匹配,匹配不了后面的
var reg = /ab/; var str = "abababab"; console.log(reg.lastIndex);// 0 console.log(reg.exec(str));// ["ab", index: 0, input: "abababab", groups: undefined] console.log(reg.lastIndex);// 0 console.log(reg.exec(str));// ["ab", index: 0, input: "abababab", groups: undefined]
往下要拓展点小知识“子表达式”
四、子表达式
想匹配一个字符串类似于这样 "xxxx" 的形式,凡是这样的形式都符合要求,
这是一个公式,
不是说四个 aaaa 就行了,
四个 bbbb 也行,四个 zzzz 也行,只要四个是一样的就行
var str = "aaaa"; 这样的形式
var reg = //; 怎么写这个正则表达式的匹配?
这个 (a) 括号还有一个意思叫做子表达式,算表达式的一种,
正常来说这个子表达式写上括号也没什么用,也不影响,
但是在特殊情况下,当用括号把这个表达式括起来之后,这个括号会记录里面匹配的内容,然后记录完之后我们会反向引用出来
/(a)\1/g
这个的“反斜杠 1”的意思叫反向引用第一个子表达式里面匹配的内容,
第一个括号里面的东西叫第一个子表达式,所以现在它匹配的是 a 和后面同样的 a
var str = "aaaa"; var reg = /(a)\1/g // 反向引用第一个子表达式里面匹配的内容 console.log(str.match(reg)); // ["aa", "aa"]
上面的比较简单,下面把子表达式里面换成元字符 \w,就变成了 \w 匹配出来的东西,后面要 copy 出一个一模一样的,叫反向引用第一个子表达式里面匹配的内容
var str = "aaaa"; var reg = /(\w)\1/g console.log(str.match(reg)); // ["aa", "aa"]
后面加三个 \1,就是三次“反向引用”第一个子表达式里面匹配的内容,匹配出来 ["aaaa"]
var str = "aaaa"; var reg = /(\w)\1\1\1/g; console.log(str.match(reg));// ["aaaa"]
如果字符串换成 "aaaabbbb" 能匹配出两个 ["aaaa", "bbbb"]
var str = "aaaabbbb"; var reg = /(\w)\1\1\1/g; console.log(str.match(reg));// ["aaaa", "bbbb"]
下面用同样的规则,能匹配出 "aabb" 吗?不能匹配返回 null,反向引用的是第一个子表达式里面匹配出的内容,也就是说后面的必须跟前面的完全雷同
var str = "aabb"; var reg = /(\w)\1\1\1/g; console.log(str.match(reg));// null
想要匹配 "aabb" 这样的形式怎么写?
\1 是反向引用第一个子表达式里面的内容,依次类推第二个子表达式是 \2
var str = "aabb"; var reg = /(\w)\1(\w)\2/g; // 反向引用第一个子表达式,反向引用第二个子表达式,保障前两个相等后两个相等 console.log(str.match(reg)); // ["aabb"]
接下了还得看正则的 exec 方法
var str = "aabb"; var reg = /(\w)\1(\w)\2/g; console.log(reg.exec(str)); // ["aabb", "a", "b", index: 0, input: "aabb", groups: undefined]
数组里多出了 "a"、"b" 两位,是第一个子表达式匹配的内容和第二个子表达式匹配的内容
而且多出来的两位是正式的数据位,什么是数据位?是挂在数组里面的索引位上的,虽然是类数组但是类数组里的正规军能当数组使。
不加通配符 g 也是这个样子的,只是不变 lastindex 而已
var str = "aabb"; var reg = /(\w)\1(\w)\2/; console.log(reg.exec(str)); // ["aabb", "a", "b", index: 0, input: "aabb", groups: undefined]
这个 exec() 方法在 jQuery 源码里还有,而且是是深度应用
五、String对象的方法
支持正则表达式的 String 对象的方法
方法 | 描述 | FF | IE |
---|---|---|---|
search | 检索与正则表达式相匹配的值。 | 1 | 4 |
match | 找到一个或多个正则表达式的匹配。 | 1 | 4 |
replace | 替换与正则表达式匹配的子串。 | 1 | 4 |
split | 把字符串分割为字符串数组。 | 1 | 4 |
match 是字符串的方法,也可以填字符串,但是一般都填正则表达式,
按照正则表达式的规则,匹配字符串里面有多少个片段,不加 g 只匹配出一个片段(要么没有要么一个),然后返回的结果类似有 exec,把第一个子表达式和第二个子表达式引用的内容也出来了
var str = "aabb"; var reg = /(\w)\1(\w)\2/; console.log(str.match(reg)); // ["aabb", "a", "b", index: 0, input: "aabb", groups: undefined]
但是一旦加修饰符 g 后,就不在有那些累赘信息了,加 g 后之只是把匹配多少个返回,什么子表达式全没有了
var str = "aabb"; var reg = /(\w)\1(\w)\2/g; console.log(str.match(reg)); // ["aabb"]
search 方法查找的,是匹配到的这个片段的位置,但凡返回的不是"-1"的都匹配成功了
var str = "aabb"; var reg = /(\w)\1(\w)\2/g; console.log(str.search(reg)); // 0
"edbaabb" 在字符串开头增加 edb 三个字母,返回匹配到片段的位置是 3
var str = "edbaabb"; var reg = /(\w)\1(\w)\2/g; console.log(str.search(reg));// 3
字符串结尾再加几个字母"cdfaabbbbee"会有变化吗?没有变化返回的还是3,search方法和exec方法不一样,它只是返回匹配的位置,不返回匹配了多少个,加不加g没区别
var str = "cdfaabbbbee"; var reg = /(\w)\1(\w)\2/g; console.log(str.search(reg)); // 3 // console.log(reg.exec(str)); // ["aabb", "a", "b", index: 3, input: "cdfaabbbbee", groups: undefined]
如果匹配不到返回-1,位置没有负一,所以负一代表错误信息
var str = "abc"; var reg = /(\w)\1(\w)\2/g; console.log(str.search(reg)); // -1
split() 方法是拆分字符串,里面可以填字符串还可以填正则表达式,按正则表达式去拆
var str = "dada1AAwc3nvBBad4CCdsdf"; var reg = /(\w)\1/g; // 如果有两个重复的字符就拆了(大写的字母都是重复的) console.log(str.split(reg)); // ["dada1", "A", "wc3nv", "B", "ad4", "C", "dsdf"]
上面写子表达式,把子表达式放到了返回信息里面
不用子表达式,用数字拆分
var str = "dada1AAwc2nvBBad3CCdsdf"; var reg = /\d/g; // 按数字拆字符串 console.log(str.split(reg)); // ["dada", "AAwc", "nvBBad", "CCdsdf"]
replace方法最后一个是最重要,最实用的字符串方法
先看正常的replace的用法,不用正则表达式写,字符串里面的a替换成b,返回结果是"ba",replace没有访问全局的能力只能访问一个,这是没有用正则表达式的缺陷
var str = "aa"; console.log(str.replace("a" ,"b")); // ba
但是如果用正则表达式就两说了,现在匹配多少个?还是改变1个,因为没加修饰符g
var reg = /a/; var str = "aa"; console.log(str.replace(reg ,"b")); // ba
写上修饰符g就全变了返回bb
var reg = /a/g; var str = "aa"; console.log(str.replace(reg ,"b")); // bb
replace方法的精华在于正则表达式上
精华到什么程度呢?把"aabb"的形式倒过来,变成"bbaa"怎么做?
var reg = /(\w)\1(\w)\2/g; // 首先把aa bb匹配出来 var str = "aabb"; newStr = str.replace(reg ,"$2$2$1$1"); // 1.替换的部分一定要写成字符串 // 2.子表达式的内容,replace()参数这里能反向引用到 // 3.怎么引用?用$来引用 // 4.$1代表第一个字表达式内容,$2代表第二个子表达式内容 // 5.$2$2$1$1返回bbaa console.log(newStr); // bbaa
还有一种更高大上的替换方式
str.replace(reg ,"$2$2$1$1") 参数不是关注字符串吗,这块不写字符串写个function函数(replace(reg, function)),但是function处理完要返回一个字符串,
可以利用function处理这个返回的字符串,有function就变的更灵活了,function不归我们调用系统会帮我们调用,系统会传参数我们得接收。
function($, $1, $2) 系统传得第一个参数是正则表达式匹配的全局,或者说是正则表达式匹配的结果,然后第二个参数是第一个子表达式匹配的内容,第三个参数是第二个子表达式匹配的内容
var reg = /(\w)\1(\w)\2/g; var str = "aabb"; console.log(str.replace(reg ,function($, $1, $2){ return $2 + $2 + $1 + $1; // bbaa }));
甚至可以再疯狂一点再加字符串"abc"可以随便拼,有了这样的function之后就变的灵活多了
var reg = /(\w)\1(\w)\2/g; var str = "aabb"; console.log(str.replace(reg ,function($, $1, $2){ return $2 + $1 + $2 + $1 + '---abc'; // baba---abc }));
现在看一个字符串方法
字符串有个方法是toUpperCase()字母变大写,toLowerCase字母变小写
var str = "aabb"; console.log(str.toUpperCase()); // AABB console.log(str.toUpperCase().toLowerCase()); // aabb
看一道非常有意思的题
字符串“the-first-name”经过处理之后变成小驼峰式写法“theFirstName”
先找规律-f是规律,把 "-f"变F,-n变成N
1) 先匹配-f "/-\w/g"
2) 匹配到"-f"替换"f",怎么把"f"单独找出来?有一个办法,$引用的是子表达式,把\w括号起来。
3). 替换成下滑线看看,返回 the_irst_ame
var reg = /-(\w)/g; var str = "the-first-name"; console.log(str.replace(reg, "_")); // -f、-n先替换成下划线 "the_irst_ame"
现在怎么变大写的F,用function第一个参数是$,用第二个参数$2
var reg = /-(\w)/g; var str = "the-first-name"; console.log(str.replace(reg, function($ ,$1){ return $1.toUpperCase(); // theFirstName }));
这是正则表达式一个高级的用法。
匹配了多少次就有多少次个function执行,reg找了两次,找第一次时执行一次function($ ,$1)里面的$ ,$1对应的是第一次的,第二次在匹配对应的是第二次的
如果想把选中的字符替换成"$"符号,直接写$是不行的,因为$1、$2代表第一个子表达式第二个子表达式是有语言意义的,如果想强制替换成$就在前面在加上一个$,就相当于在这里面的转义字符。
var reg = /(\w+)\s(\w+)/; var str = "cainiao gaoshou"; var strNew = str.replace(reg ,'$$ $$'); console.log(strNew);
六、正向预查
要选择一个字符串上面的 a,
1. 但这个字符串上面有好几个 a,要选择的这 a 后面要跟着 b 的那个 a 符合要求,
2. 选的不是 b,b 就作为一个限制条件,选的还是 a。这个就叫“正向预查”或者叫“正向断言”
var str = "abaaaaa"; var reg = /a(?=b)/g; // a后面跟着(?=b)代表,a后面跟着的是b,但b不参与选择,b只参与修饰、限定 console.log(str.match(reg)); // ["a"]
非正向预查 /a(?!b)/g,意思后面不是跟着 b 的那个 a,那就多了!
var str = "abaaaaa"; var reg = /a(?!b)/g; console.log(str.match(reg)); // ["a", "a", "a", "a", "a"]
七、非贪婪匹配
“非贪婪匹配”就是能少就尽量别多,
正常的正则表达式的匹配原则是贪婪匹配有多就不少
var str = "aaaaa"; var reg = /a+/g; console.log(str.match(reg));//["aaaaa"]
可以打破贪婪匹配的原则变成“非贪婪匹配”,
非贪婪匹配就是能少就尽量别多怎么办呢?在任何一个量词的后面在多加一个问号,这样就变成“非贪婪匹配”能少就不多了
var str = "aaaaa"; var reg = /a+?/g; console.log(str.match(reg)); // 能一个就不会多个的 ["a", "a", "a", "a", "a"]
这个问号可以打破任何东西,{1,3} 正常的返回 ["aaa", "aa"]
var str = "aaaaa"; var reg = /a{1,3}/g; console.log(str.match(reg));//["aaa", "aa"]
打破贪婪匹配加 ? 问号,有1就不取3不取2
var str = "aaaaa"; var reg = /a{1,3}?/g; // 加?号有一就不取3 console.log(str.match(reg)); // ["a", "a", "a", "a", "a"]
如果问号后面加问号呢?
第一个问号代表量词,第二个问号是取消贪婪匹配,
问号是0到1的意思,那再加一个问号,那只取0不取1,能取0就不取1
var str = "aaaaa"; var reg = /a??/g; console.log(str.match(reg)); // ["", "", "", "", "", ""]
“*号加问号”跟问号加问号差不多,只取 0 不取 1
var str = "aaaaa"; var reg = /a*?/g; console.log(str.match(reg));// ["", "", "", "", "", ""]
八、练习
空格匹配
字符串去重
百度面试题
1、匹配空格
如果要匹配空格怎么办?
不用写 /s 这么麻烦,
直接写空格就行了,我们在正则表达式里写的任何一个东西,都算是正则匹配的内容
var str = "aa aaa"; var reg = / /g; console.log(str.match(reg));// [" "]
2、转义字符
因为 $1、$2 代表第一个反向引用第一个子表达式,第二个子表达式这是有语法意义的
如果强替换成 $ 前面在写一个 $ 相当于转义字符
var reg = /(w+)\s(\w+)/; var str = 'cainiao gaoshou'; var newStr = str.replace(reg, '$$ $$'); console.log(newStr); // cainiao gaoshou
如果想匹配出一个 \ 反斜杠怎么办?正则里要写两个
var str = "aa\\aaa"; var reg = /\\/g; // 写一个不行要写\\两个斜杠 console.log(str.match(reg));//["\"]
如果想正则里面想匹配 ? 问号,
必须加转义 \? 因为问号本身就有语法含义
var str = "aa?aaa"; var reg = /\?/g; console.log(str.match(reg));//["?"]
*、+、- 也都类似
扩号也类似,括号也有语法含义
var str = "aa?a(aa"; var reg = /\(/g; console.log(str.match(reg)); // ["("]
因为 $1、$2 代表第一个反向引用第一个子表达式,第二个子表达式这是有语法意义的
如果强替换成 $ 前面在写一个 $ 相当于转义字符
var reg = /(w+)\s(\w+)/; var str = 'cainiao gaoshou'; var newStr = str.replace(reg, '$$ $$'); console.log(newStr); // cainiao gaoshou
3、字符串去重
匹配一串,用 ( \w ) 反向引用一个或者多个,
匹配出来三个片段,然后让每次匹配的字表达 $1 代替一串就行了
var str = "aaaaabbbbbbbcccccccc"; var reg = /(\w)\1*/g; // 匹配一串重复的 console.log(str.replace(reg, "$1")); // abc
4、百度面试
2014年百度面试最后一道题,非常有意思
从后位往前面查,每隔三位打一个点,要求变成这样的 10.000.000.000
var str = "10000000000";
怎么把点放进去?
肯定要换东西,换的是空
什么样的空?
后面有三个数字的空,只能匹配出一个
10 000 000空000 这个空后面有三个数字
10 000空000 000 这个空后面有六个数字
10空000 000 000 这个空后面有九个数字
规律是空后面的数字的个数,是三个的倍数
首先从后往前查,必须有 $ 以什么什么结尾
var str = "100000000"; var reg = /($)/g; console.log(str.replace(reg, ".")); // 100000000.
我们要匹配空,这个空以三个0结尾
先解决三个 0 的问题
var str = "1000000"; var reg = /(\d{3}$)/g; console.log(str.replace(reg, ".")); // 1000000.
3 位数字还得是 1 到多个,
也就是这 (\d{3}) 里面的三位数字要1到多个,用 + 加号表示1到多个
var str = "100000000"; var reg = /((\d{3})+$)/g; console.log(str.replace(reg, ".")); // .
空后面跟着三个数字,加一个正向预查,
正向预查后面是 (\d{3})+ 表示三个数字个数的倍数
var str = "100000000"; var reg = /(?=(\d{3})+$)/g; console.log(str.replace(reg, ".")); //.100.000.000
最后前面的空,要是非单词 \B
var str = "100000000"; var reg = /(?=(\B)(\d{3})+$)/g; console.log(str.replace(reg, ".")); // 100.000.000
过程
首先从后往前查
查三个位的数字,替换为空
这个空还不能是单词边界