JavaScript 正则 上
正则表达式(RegExpression)的作用,
匹配特殊字符或有特殊搭配原则的字符的最佳选择。
一、RegExp
两种创建方式
1. 直接量
2. new RegExp();
个人推荐用直接量
正则表达式和 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/ 还能匹配吗?
不能匹配,因为字符串里面没有 abce 连着的片段,返回 false
var reg = /abce/; var str = 'abcd'; console.log(reg.test(str)); // false
如果字符串后面加了一个 e,这样 "abcde" 成立吗?
不成立,因为字符没有挨着,abc 与 e 中间隔了一个 d
var reg = /abce/; var str = 'abcde'; console.log(reg.test(str)); // false
上面是基本的正则表达式,
然后正则表达式后面还可以加一些属性,这些属性分别有不同的意义
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
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 的地方换行了
在不换行之前,看一个小需求:
匹配字符串 "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()
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]
\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));// ["若", "非", "生", "活", "所", "迫", " ", "谁", "愿", "一", "身", "才", "华"]