Go to comments

JavaScript 正则 上

正则表达式(RegExpression)的作用,

匹配特殊字符或有特殊搭配原则的字符的最佳选择。

一、RegExp


W3C 手册


正则表达式和Date()虽然不是一个东西但本质上类似,


什么叫本质上类似呢?

new Date()能创建时间对象,

正则表达式不是一个单独的规则,它是一个规则的对象,这个对象里包含的信息是规则,所以说正则表达式也要创建出来


创建就两种方法,

第一种方法,正则表达式字面量(直接量),正则表达式的英文名是RegExp

第二种方法,new RegExp()

1、正则表达式第一种创建方式"直接量"(也叫字面量)

字面量的形式是两个斜线(注释的那个斜线)在两个斜线中间写内容,如果写了 /abc/  就代表正则表达式abc

var reg = /abc/; // 这样一个正则表达式就完事了,这是第一种创建方式


这个正则表达式 /abc/ 的基本功能,代表匹配字符串规则叫abc,是什么意思呢?

1). 来一个字符串是"abcd",现在通过正则表达式的test()方法,可以把字符串"abcd"放进去 reg.test("abcd") 

2). 放进去的意思是“正则表达式”要测验一下,这个字符串"abcd"里面含不含有我规定的片段

3). 我规定的片段是 /abc字符片段,这个字符串必须是abc,这三字母必须挨着,顺序还必须这么写,还都得是小写


像abc这样的字符串,字符串"abcd"里面有没有?有,字符串 "abcd开头是abc,所以必然返回true

var reg = /abc/;

var str = "abcd";

console.log(reg.test(str)); // true


如果正则后面加一个字母e变成 /abce/ 还能匹配吗?

var reg = /abce/;

var str = 'abcd';

console.log(reg.test(str)); // false

不能匹配,因为字符串里面没有 abce 连着的片段,返回false


如果字符串后面加了一个e,"abcde" 这样成立吗?

var reg = /abce/;

var str = 'abcde';

console.log(reg.test(str)); // false

不成立,因为字符没有挨着(abc与e中间隔了一个d)


上面是基本的正则表达式,然后正则表达式后面还可以加一些属性,这些属性分别有不同的意义

2、三个属性i、g、m

如果正则表达式后面加一个 i ,这个正则表达式可以忽略大小写,i是ignoreCase的缩写翻译过来是忽视大小写


因为正则表达式忽视大小写,所以下面正则表达式成立返回true

var reg = /abce/i;

var str = 'ABCEd';

console.log(reg.test(str)); // true


不加 i 是不行的返回false

var reg = /abce/;

var str = 'ABCEd';

console.log(reg.test(str)); // false


这是一个属性的意义,先感受一下 i 还有g、m,

这三个属性还可以写到一起,还可以自由组合,也可以单独来写,

g、m两个属性的意思下面会学到


3、正则表达式第二种创建方法 new RegExp()

第二种创建方法和对象类似(new的方法) new RegExp() ,这是RegExp()他的构造函数,里面的第一个参数就是它的规则,当然这个规则是用字符串来写

var reg = new RegExp("abc"); // 这是正则表达式 abc

var str = "abcd";            // 这是字符串"abcd"

console.log(reg.test(reg));  // true


RegExp()第二个参数里面写的是属性,属性同样放到字符串里面,如果没有属性可以不用写

var reg = new RegExp("abc" ,"igm");

var str = "abcd";

console.log(reg.test(reg)); // true

W3C 手册

4、一个小的区别和例子

读下面这二段话:

一个新的RegExp对象,具有指定的模式和标志。如果参数 pattern 是正则表达式而不是字符串,那么RegExp()构造函数将用与指定的 RegExp 相同的模式和标志创建一个新的 RegExp 对象。

如果不用 new 运算符,而将 RegExp() 作为函数调用,那么它的行为与用 new 运算符调用时一样,只是当 pattern 是正则表达式时,它只返回 pattern,而不再创建一个新的 RegExp 对象。


看下面例子,就知道这两句话说的是什么了

var reg1 = /abce/m;         // 1. 已知的正则表达式"reg1"

var reg2 = new RegExp(reg1);// 2. "new RegExp(reg1)"第一个参数除了可以放字符串之外,还可以把已知的正则表达式reg1放里面
							// 3. 放里面之后,reg1和reg2虽然说形式上一样,但是却是两个不同的正则表达式,两个独立的正则表达式

console.log(reg1);          //    打印reg1返回 /abce/m
console.log(reg2);          //    打印reg2返回 /abce/m
							// 4. reg1和reg2都一样
							
reg1.abc = 123;             // 5. 但是给reg1加属性abc,属性值是123

console.log(reg2.abc);      // 6. reg2访问abc属性是没有的,返回undefined

reg1和reg2互相独立,虽然它俩样子一样但是两个人,这是正常的情况。

上面的“读下面这二段话”论述了一个非正常的情况。

var reg1 = /abce/m;
var reg2 = RegExp(reg1);// 1. "RegExp(reg1)"把new去了括号里面放了reg1,虽然也是利用原有的创建一个正则表达式
                        // 2. 但是现在创建出的reg1和reg2本质上是同一个人,只不过是不同的引用而已,怎么来验证呢?
						
console.log(reg1);      // 打印reg1返回 /abce/m
console.log(reg2);      // 打印reg2返回 /abce/m

reg1.abc = 123          // 3. 给reg1加abc属性

console.log(reg2.abc);  // 4. 打印reg2也有abc这个属性返回 123

上面两大段论述的是,如果RegExp(reg1)不加new的话,把一个正则表达式reg1放到里面,返回的只是这个正则表达式的引用,就是两个人拿走同样的钥匙指向一个房间


二、正则表达式的三个属性(修饰符)

修饰符描述
i执行对大小写不敏感的匹配。
g执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m执行多行匹配。


i 忽略大小写

上面说了,i是忽视匹配时候的大小写

g 全局匹配

全局匹配是什么意思呢?

现在用正则表达式上的test()方法已经不太够了,要用字符串上的match()方法,str.match(reg)里面传正则表达式,它会按照正则表达式的规则,把字符串里面符合正则的片段给挑选出来。

var reg = /ab/;

var str = "ababababa";

console.log(str.match(reg)); // ["ab", index: 0, input: "ababababa", groups: undefined]

str.match(/ab/)方法把"ab"挑选了出来,但是只返回了一个ab,而且是放到数组里面的。

如果正则后面加上一个g /ab/g ,它会把找到的所有情况都给返回,所以g叫作globe全局匹配,匹配完一个之后还不够,还继续往后看,看里面有没有符合要求的片段,这是g的作用叫“全局匹配”。

var reg = /ab/g;

var str = "ababababa";

console.log(str.match(reg)); // ["ab", "ab", "ab", "ab"]

m 执行多行匹配

首先“多行匹配”要先知道字符可以多行,字符串怎么才能多行?不是写起来多行,插入 \n 就多行了。

var str = "ababa\nababab";

插入 \n 的地方换行了

image.png


在不换行之前,看一个小需求:

匹配字符串 "abcdea" 里面的a,  /a/g  正则加修饰符g(全局匹配),能匹配多少个a?匹配出两个a,因为字符串里面有两个a("abcdea" )

reg = /a/g;

var str = "abcdea";

console.log(str.match(reg));// ["a", "a"]

现在把这个要求变的苛刻一些,匹配以a开头的这个a("abcdea")怎么办呢?

正则前面加一个尖角号 /^a/g ,这个a可就不是不是一般的a了,必须是这个字符串的开头的a。现在符合要求的就一个a了,后面第二个a就不符合要求了,虽然有"g"全局匹配,但是后面的a不符合要求了。

reg = /^a/g;

var str = "abcdea";

console.log(str.match(reg)); // ["a"]


现在在字符串第二个a前面加一个换行\n符  "abcde\na"  就变成多行字符串了,多行字符串里再找以a开头的a,现在能找到几个a?

还是找到一个a,因为现在正则还没有多行匹配的功能。

reg = /^a/g;

var str = "abcde\na";

console.log(str.match(reg));//["a"]


直到加上修饰符m后  /^a/gm  才具备多行匹配的功能,才认为第二个a是另一行的开头,正则结果返回两个a,这是m修饰符的基本意义

reg = /^a/gm; // 加修饰符m后具备多行匹配的功能

var str = "abcde\na";

console.log(str.match(reg)); // ["a", "a"]


PS:

一个始终不被善待的人,最能识别善良,也最尊重善良 —— 严歌苓

有些人记吃不记打,他不是这样


学正规语法之前,先渗透两个方法

方法一:

正则表达式上有个方法test()

这个 reg.test() 只能判断字符串上有没有符合要求的片段,返回结果只有true或false


方法二:

还有字符串上的match()

而这个 str.match(reg) 可以把所有东西匹配出来返回给我们,比test方法更直观而且还可以告诉我们匹配多少个


三、表达式

方括号 [] 用于查找某个范围内的字符

表达式描述
[abc]查找方括号之间的任何字符。
[^abc]查找任何不在方括号之间的字符。
[0-9]查找任何从 0 至 9 的数字。
[a-z]查找任何从小写 a 到小写 z 的字符。
[A-Z]查找任何从大写 A 到大写 Z 的字符。
[A-z]查找任何从大写 A 到小写 z 的字符。
[adgk]查找给定集合内的任何字符。
[^adgk]查找给定集合外的任何字符。
(red|blue|green)查找任何指定的选项。


匹配下面字符串中有没有这样的情况,第一位是数字、第二位还是数字、第三位还是数字,也就是三个数字相连接的情况

var str = '12309asd754fwe2astw232dfa'


不能这样 /123/ 写正则了,这样固定了只能匹配"123",

让第一位是可变的数字,第二位是可变的数字,第三位也可变数字怎么办呢?

1、用"表达式"来解决,这个表达式是一对方括号[]

一个方括号/[ ]/g 代表一位,写三个方括号 /[][][]/g 就代表三位,三位方括号 /[][][]/g 和三位/123/g 一样就是三个。


一个表达式 /[]/g 里面可以填的是什么呢?

里面填的就是这个表达式可以取到的范围(这一位值可以取到的范围)比如里面填  /[0123456789]/g 这一位的范围是 0 到 9,


/[0123456789][0123456789][0123456789]/g

三个范围组成一个正则表达式,一个范围代表第一值,从这个范围里任取一个,红色的部分是符合的片段"12309asd7546fwe2astw232dfa"

var reg = /[0123456789][0123456789][0123456789]/g;

var str = '12309asd7546fwe2astw232dfa'

console.log(str.match(reg));// ["123", "754", "232"]


"表达式"里面可以填任意的东西,

比如字符串"abcd",  /[ab][cd][d]/g; 这样的表达式能匹配出来吗?

[ab]  第一位代表着区间,只能取a或者b

[cd]  第二位只能取,c或者d

[d]    第三位只能取d

字符串"abcd"中的"bcd"被匹配出来,这是表达式的一个基本应用

var reg = /[ab][cd][d]/g;

var str = 'abcd';

console.log(str.match(reg)); // ["bcd"]


记住,一个表达式代表一位,里面填的东西就是区间,而且这个区间可以填的很没有下限,怎么填都行可以玩赖的填

/[0-9]/g               意思是0到9

/[0-9A-Za-z]/g    大A到大Z、小a到小z

/[0-9A-z]/g         还可以直接"大A"到"小z",因为ASCII大A是65,它是按照ASCII的顺序排的,"大A"到"小z"就把52个英文字母全包括了

/[0-9A-z]/g         0到9、大A到小z,基本上囊括了键盘上所有的字符了


字符串"ab1cd"中间加一个1

[0-9A-z]   第一个区间匹配1

[cd]          第二个区间匹配c

[d]            第三个区间匹配d

匹配出来 "1cd"

var reg = /[0-9A-z][cd][d]/g;

var str = 'ab1cd';

console.log(str.match(reg)); // ["1cd"]

2、"^"符

 /[^]/g 

这个尖角号叫做,尖角号在表达式里面的意思,和放到表达式外面不一样,

放到表达式里面的意思代表"非",就是叹号、"除了"的意思。


 /[^a][^b]/g  这区间老大了,能匹配出几个?

第一位不是a都行

第二位不是b都行

全局匹配返回一个数组 ["b1", "cd"]

var reg = /[^a][^b]/g;

var str = 'ab1cd';

console.log(str.match(reg)); // ["b1", "cd"]

返回的是一个数组,有属性"length: 2",而且__proto__指向的是Array()

image.png

3、(red|blue|green)

 () 括号本不应该是表达式的表达式,它写在表达式里面了,括号里面还写了一个中划线,


下面括号和数学的括号没什么区别,写括号跟没写一样的,但数学的括号起到优先计算的作用(其实括号不应该归到表达式里面的)

var reg = /(abc)/g;


括号里面可以加一个竖线"|",双竖线叫"或",单竖线是正则表达式的"或"

var reg = /(abc|bcd)/g;


  /(abc|bcd)/g  abc或bcd,意思是会先进行一个计算,字符串"abc"匹配成功

var reg = /(abc|bcd)/g;

var str = 'abc';

console.log(str.match(reg)); // ["abc"]


字符串"bcd"也可以匹配成功

var reg = /(abc|bcd)/g;

var str = 'bcd';

console.log(str.match(reg)); // ["bcd"]


 /(abc|bcd)/g 

就变成了一个类似于表达式的区间,只不过这个区间是连着的要么abc要么bcd,

所以从这个层次来理解括号也算个表达式,只不过它不拿[]方括号写,用小括号()写的。


然后(abc|bcd)后面加一个0至9  /(abc|bcd)[0-9]/g ,字符串"bcd"后面加一个数字2。意思是abc加一位数字,或者bcd加一位数字都可以通过匹配,所以字符串"bcd2"可以通过匹配

var reg = /(abc|bcd)[0-9]/g;

var str = 'bcd2';

console.log(str.match(reg)); // ["bcd2"]


四、元字符

元字符(Metacharacter)是拥有特殊含义的字符:

元字符描述
.查找单个字符,除了换行和行结束符。
\w查找单词字符。
\W查找非单词字符。
\d查找数字。
\D查找非数字字符。
\s查找空白字符。
\S查找非空白字符。
\b匹配单词边界。
\B匹配非单词边界。
\0查找 NUL 字符
\n查找换行符。
\f查找换页符。
\r查找回车符
\t查找制表符。
\v查找垂直制表符。
\xxx查找以八进制数 xxx 规定的字符。
\xdd查找以十六进制数 dd 规定的字符。
\uxxxx查找以十六进制数 xxxx 规定的 Unicode 字符。


"元字符"和"表达式"是一个东西,只不过元字符用起来更加风骚一些,飘逸一些,表达式还用写中括号[],元字符直接写就可以了


PS: 

本人学识渊博、经验丰富,代码风骚、效率恐怖……


 \w 

正则表达式里面写 /\w/ 它也代表一位, \w完全等于[0-9A-z_],w是world的意思,叫单词字符或叫字母字符,有人把它理解成键盘字符都行。

它代表的区间是0到9、大A到小z、还有下划线


 \W 

杠大写的W代表的是,非的杠小写的w,和小写的杠\w正好取补集, \W 完全等于[^\w]  


下面正则是 /\wcd2/g  能匹配字符串"bcd2"吗?必须能匹配返回 ["bcd2"]

var reg = /\wcd2/g; // 小写w

var str = "bcd2";

console.log(str.match(reg)); // ["bcd2"]

小写的 \w 的意思是取这个[0-9A-z_]的区间,它也是一个表达式,就是个表达式 [0-9A-z_] 的翻译版本


大写的 \W 是除去这些 [0-9A-z_] 以外的,字符串"bcd2"就匹配不出来了返回null

var reg = /\Wcd2/; // 大写的\W

var str = "bcd2";

console.log(str.match(reg)); // null


字符串上面加 星号 能匹配吗?*号属于大写\W的范畴,所以能匹配出来返回 ["*cd2"]

var reg = /\Wcd2/g;

var str = "b*cd2";

console.log(str.match(reg)); // ["*cd2"]


 \d 

小写的d代表[0-9]  \d <---> [0-9] 


 \D 

依据经验大写D是非0到9   \D <---> [^\d] 


小写的 \d 代表数字,三个 /\d\d\d/g 匹配字符串"123",三位数字匹配成功,小d和大D这个比较简单

var reg = /\d\d\d/g;

var str = "123";

console.log(str.match(reg)); // ["123"]


现在这么写 /[\w\d]/g 小写w小写d,匹配一位的字符串"s"能成功吗?小写的\w代表是[0-9A-z_]都没必要写小\d,小\w就能包含"s"了

var reg = /[\w\d]/g; // 小写的w、小写的d

var str = "s"; // 字符串"s"

console.log(str.match(reg)); // ["s"]

上面这个意思是告诉我们可以在表达式里面写“元字符”而且可以写多个


 \s 

空白字符的意思(包含空格),能展示空白的东西多了,首先空格符算、制表符、回车符、换行符、垂直换行符、换页符

\n  换行符

\f   换页符

\r   回车符(行结束符)

\t   制表符

\v  垂直制表符

一个\s等于这五个加空格   \s <---> [\t\n\r\v\f ],在正则表达式里写一个空格就真的代表匹配是一个空格


 \S 

大写S,非小写的s  \S <---> [^\S]


W3C 手册


 \b 

单词边界


 \B 

非单词边界


什么叫"单词边界"?

之前写的字符串都中规中矩,写几个不正常的"abc cde fgh",这个字符串中加两个空格,代表三个单词,这个字符串有几个单词边界?

有六个单词边界"1abc2 3cde4 5fgh6"(红色的数字是单词边界),\b匹配的就是单词边界


现在匹配一个"c",但是前面要跟着一个单词边界 /\bc/g ,也就是这个"c"要是一个单词的边界,可以说是一个单词开头,但是不能说是一个行开头

"c"是单词边界后面跟着de  /\bcde/g  能不能匹配出来?可以匹配字符串"abc cde fgh"中的cde出来返回["cde"]

var reg = /\bcde/g;

var str = "abc cde fgh";

console.log(str.match(reg)); // ["cde"]


单独正则一个"c"的开头,字符串"abc cde fgh"中的也是匹配出一个c(红色标注的c),不会出两个c

var reg = /\bc/g;

var str = "abc cde fgh";

console.log(str.match(reg)); // ["c"]


如果 /\bcde/g  的cde后面再加一个单词边界 /\bcde\b/g ,就是 cde 前后都是单词边界,字符串"abc cde fgh"中的cde符合要求吗?符合要求返回 ["cde"]

var reg = /\bcde\b/g;

var str = "abc cde fgh";

console.log(str.match(reg)); // ["cde"]


接下来把字符串"abc cdefgh"第二个空格删了还符合吗?不符合了返回null

var reg = /\bcde\b/g;

var str = "abc cdefgh";

console.log(str.match(reg)); // null


接上题,在 /\bcde\B/g 后面加一个\B非单词边界,前面单词边界,后面是非单词边界符合匹配返回 ["cde"]

var reg = /\bcde\B/g;

var str = "abc cdefgh";

console.log(str.match(reg)); // ["cde"]


 \n   回车换行

 \f  

 \r  

 \t    制表符(tab键)

 \v  


在字符串里面打一个tab键"abc  cdefgh",正则规则\t加上c  /\tc/g   能匹配出来吗?匹配不出来返回null

var reg = /\tc/g;

var str = "abc  cdefgh" // 在字符串里面按一个tab键

console.log(str.match(reg)); // null


\t 匹配的不是视觉上的,而是真实有的内容"abc\tcdefgh"这才行匹配返回 [" c"]

var reg = /\tc/g;

var str = "abc\tcdefgh"

console.log(str.match(reg));// ["	c"]


匹配 \n 字符串上也要写上"abc\ncde"才能匹配的里面是回车换行["↵c"]

var reg = /\nc/g;

var str = "abc\ncdefgh"

console.log(str.match(reg));//["↵c"]

其它的\v、\r,\t、\f都用不着,"\s"能代表的真实有意义的无非就是一个空格另一个是换行


Unicode编码

然后说一下Unicode编码

Unicode编码一般是六位这样 \u0000

Unicode编码和ASCII码表达的是一个意思,一个Unicode编码对应一个字符,只不过ASCII码能对应一些特殊的简单的比如黑桃、红桃、方片、abc什么的,

Unicode编码升级了一下它能代表很多种语言,一个Unicode编码也可以代表一个汉字了。


Unicode编码有个表达形式

第一种表达形式是四位的Unicode编码 \u0000 后面这四位别小瞧它,ASCII码是七位、八位(ASCII是八位的)是二进制,由于二进制太长了Unicode编码把每一位都变成十六进制了,

\u0000 后面这的四位是十六进制的四位数,它能表示一定的东西,然而这是简单的Unicode编码,当然简单Unicode就已经能把汉字给包进去了,汉字一共就两三万个,


还有一个升级版的Unicoed编码,所以经常看到Unicode编码有这样的 \u0affff、这样的 \u101abe 的形式,这是什么意思呢?


Unicode编码是分层的,也就是说分不同空间,

最简单的层就是第0层

第一层 \u010000  -  \u01ffff

第二层 \u020000  -  \u02ffff

……

一直到第十六层,就是现在已开发出来的

十六层 \ul00000  -  \u10ffff


也就是说前两位代表多少层,后面四位代表范围,不同的层包含的信息不一样,

我们经常用的就是第一层,基本忽略掉前面的 01(\u0000)第二层必须要写最多十六层,\u100000由于是十六进制所以前面的10(一零)代表16


Unicode编码可以表示汉字,百度所搜 汉字unicode 有个在线转码器,字符串"葛兰"的Unicode编码是 \u845b\u5170 把它拷贝到正则表达式里面

var reg = /\u845b\u5170/g;

var str = "葛兰";

console.log(str.match(reg)); // ["葛兰"]


Unicode编码可以匹配汉字,所以不用担心能匹配英文,不能匹配汉字的事,Unicode编码什么都能匹配,

而且Unicode和ASCII编码一样是有区间的,ASCII编码区间0-255,Unicode编码也有区间0000-ffff


把 \u3000-\ua000 这个区间写到正则里面,字符串"葛兰"也能匹配出来,是分开匹配的,这是区间生效了,所以Unicode编码也可以写区间,它比ascii码更高级

var reg = /[\u3000-\ua000]/g;

var str = "葛兰";

console.log(str.match(reg)); // ["葛", "兰"]


如果Unicode编码  \u0000 到 \uffff  全f这个区间,还有什么是匹配不出来的吗?

Unicode编码包含了一切字符一切特殊字符(钩尖K扑克牌都有,方片尖,红桃尖……), /[\u0000-\uffff]/g 这么写没什么是匹配不出来的,这么写代表一切代表All

var reg = /[\u0000-\uffff]/g;

var str = "泰然处事,无所不能";

console.log(str.match(reg));//["泰", "然", "处", "事", ",", "无", "所", "不", "能"]


一个问题

现在真想匹配一切字符,还有什么更好的方法能代表一切的集合?

有很多种组合,有个更好的写法能代表一切  [\s\S] 补集 或者 [/d/D] ,原有集合的补集和它自己代表all一切,比如世界上的一个电脑和除了这个电脑的一切东西,这两个放到一起就是任何东西代表所有

var reg = /[\s\S]/g;

var str = "葵花在手,江山我有";

console.log(str.match(reg));// ["葵", "花", "在", "手", ",", "江", "山", "有", "我"]


或者 [\d\D] 一样的

var reg = /[\d\D]/g;

var str = "葵花在手,江山我有";

console.log(str.match(reg));//  ["葵", "花", "在", "手", ",", "江", "山", "我", "有"]


补集,

这个世界上的物体一个电脑,和除了这个电脑的所有东西,把这两个东西放到一起就是任何的东西,这样代表所有。


元字符还差一个"点",这个点特别霸道,

 .  点能查找单个字符,除了"换行"和"行结束符"  . <---> [^\r\n] ,除非字符串里有这两\r\n,否则这个"."点代表一切了


 /./g  能匹配出字符串的每一个字符

var reg = /./g;

var str = "若非生活所迫,谁愿一身才华";

console.log(str.match(reg));// ["若", "非", "生", "活", "所", "迫", ",", "谁", "愿", "一", "身", "才", "华"]


字符串再加一个空格呢,空格也能匹配出来

var reg = /./g;

var str = "若非生活所迫 谁愿一身才华"; // 逗号换成空格

console.log(str.match(reg));// ["若", "非", "生", "活", "所", "迫", " ", "谁", "愿", "一", "身", "才", "华"]



Leave a comment 0 Comments.

Leave a Reply

换一张