Go to comments

JavaScript 正则 下

W3C 手册

一、量词

量词很简单是代表数量的词

量词描述
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对象的属性

属性什么意思呢?

正则表达式是一个对象,对象上势必会有些属性的

属性描述FFIE
globalRegExp 对象是否具有标志 g14
ignoreCaseRegExp 对象是否具有标志 i14
lastIndex一个整数,标示开始下一次匹配的字符位置14
multilineRegExp 对象是否具有标志 m14
source正则表达式的源文本14


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 方法时候再说


W3C 手册


三、RegExp对象的方法

方法描述FFIE
compile编译正则表达式。14
exec检索字符串中指定的值。返回找到的值,并确定其位置。14
test检索字符串中指定的值。返回 true 或 false。14


正则表达式的 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 源码里还有,而且是是深度应用


W3C 手册


五、String对象的方法

支持正则表达式的 String 对象的方法

方法描述FFIE
search检索与正则表达式相匹配的值。14
match找到一个或多个正则表达式的匹配。14
replace替换与正则表达式匹配的子串。14
split把字符串分割为字符串数组。14


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 这个空后面有六个数字

10000 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


过程

首先从后往前查

查三个位的数字,替换为空

这个空还不能是单词边界



Leave a comment 0 Comments.

Leave a Reply

换一张