目录:
1、什么是正则
2、字符的理解
3、循环与重复
4、位置边界
5、子表达式
6、逻辑处理
1、什么是正则
正则表达式本质上就是一个用于字符串模式匹配的工具,实现字符串的搜索和替换功能。
从其命名我们可以看出来,它是一种用于描述某种规则的表达式。
我们要学习的,就是其内部的搜索及替换的功能,至于其底层实现机制,并不深究。
2、字符的理解
正则表达式根本上是由各种字符组成的,其分为字符和元字符:
字符:就是基础的计算机字符编码,通常正则表达式里面使用的就是数字、英文字母。
元字符:就是用来表示特殊语义的字符。如^表示非,|表示或等。
正则表达式就是由这两种字符来组成各种实际的规则。
2.1 单个字符匹配
最简单的正则表达式就由数字和字母组成。如我想匹配 'banana'中的 'b' 这个字符,那我直接使用 /b/ 这个正则就行了,但是我们用 /a/ 就会匹配到所有的 'a' 字符。
但是如果我们想要匹配特殊的字符,如 '*' 这个字符本身是特殊字符,我们就需要转义字符 \ 来让特殊字符失去原本的含义。
/\*/ 就是匹配 '*'这个字符
我们还可以用 \ 让原本不是特殊字符的字符,让其具有特殊的含义。如我们要匹配空格、换行、回退等符号的时候,可以用下面这张表记忆:
2.2 多个字符匹配
单个字符的匹配的一对一的,也就是说只能匹配一个结果,这显然不够用。正则表达式又引入了集合区间和通配符的方式实现一对多的匹配,即一个正则匹配多个不同的字符。
集合区间用 [] 表示,如:/[123]/ 能匹配字符串中的所有 1 2 3 字符。可以使用元区间 - 来表示集合区间的范围,如:/[0-9]/ 能匹配字符串中所有的数字,/[a-z]/ 则可以匹配所有的英文小写字母。
此外,正则表达式还衍生了一批用于匹配区间的简便特殊正则正则表达式,如下:
3、循环与重复
上面讲的是一对一和一对多的字符匹配,这里要讲的就是匹配字符的规则。
所谓的同时匹配多种字符,就是控制正则表达式中某字符的出现次数,
出现次数分为:0次、1次、无数次、固定次数。
3.1 匹配0次或1次
元字符 '?' 表示匹配其前面的字符0次或1次,如:正则表达式 /colou?r/ 就能同时匹配字符串 'color' 和 'colour', 它表示 'u' 可以出现0次或1次。
3.2 匹配0次或多次
元字符 '*' 用来表示匹配0个字符或无数个字符.
3.3 匹配1次或多次
元字符 '+' 适用于要匹配同个字符出现1次或多次的情况。
3.4 匹配特定次数
元字符 '{}' 可以匹配特定次数的字符,如: /b{3}/ 表示我想匹配连续的3个b,它还有其他的衍生规则:
- {x}: x次
- {min, max}: 介于min次到max次之间
- {min, }: 至少min次
- {0, max}: 至多max次
最终,对循环与重复的匹配做个总结:
4、位置边界
所谓位置边界的匹配,就是在长文本字符串中,限制查找字符的位置条件。如我们只想在单词的开头或结尾查找字符等。
4.1 单词边界
单词边界就是只匹配单独的单词。比较常见的场景是在特定的文章或句子中匹配特定单词。如:
The cat scattered his food all over the room.
如果我用 /cat/ 去匹配文章中的单词,最终匹配还会多余地匹配到 'scattered' 这个单词。这时我们用特殊字符 \b 去包裹我们想设置边界的单词,如: /\bcat\b/,这样就能只匹配到 'cat' 单词了。
4.2 字符串边界
上面说的是匹配单词,但有时我们需要匹配一整条字符串,需要如何做呢?
元字符 '^' 可以用来匹配字符串的开头。而元字符 '$' 可以用来匹配字符串的末尾。
另外,要匹配整条字符串,必须要避免换行符的干扰,这就需要在正则表达式的最后加上字母 'm',这表示采用多行模式。
举个例子,我要匹配下面这段文字中的 ' I am the rain man' 这段文字:
until that day,
he finally told me that,
' I am the rain man'
就要使用正则表达式 /^I am the rain man$/m.
除了多行模式,正则匹配还有其他的匹配模式,最终整理本部分内容如下:
5、子表达式
上面说的是最基础的字符匹配的内容,接下来要说的是正则子表达式。
其核心思想是把正则表达式复杂化。从简单到复杂的正则表达式演变通常要采用分组、回溯引用和逻辑处理的思想。利用这三种规则,可以推演出无限复杂的正则表达式。
5.1、分组
分组是子表达式的核心思想,其原理是:用'()'包含的正则表达式为一组子表达式,多个'()'包含的子表达式能组合成复杂的正则表达式。
5.2、 回溯引用
所谓回溯引用,指的是后面的子表达式复用前面已经匹配到的子字符串。可以理解为变量使用。其使用方法如:\1,\2 分别表示引用的第一个和第二个子表达式。而 \0 表示整个表达式。
回溯引用经常被用在替换字符串中,用 '$1' '$2' 表示要替换的字符串,如下:
let str = 'cad cae gg'
let str1 = str.replace(/(ca)d/g, '$1e')
console.log(str1); //cae cae gg
其操作等价于:
let str1 = str.replace(/(ca)d/g, 'cae')
有时我们想限制回溯引用的适用范围,可以用前向/后向查找实现。
前向查找
是用来限制后缀的,通过(?=regex)的子表达式形式来限制后缀的内容,实现前向查找。
如:happy happily这两个单词,我想获得以happy开头的副词,就可以使用happ(?=ily)来匹配。
通俗理解,就是匹配到带 'ily'后缀的 'happ' 部分,具体看下面代码:
let str = 'happy happily'
let str1 = str.replace(/happ(?=ily)/, 'haha')
console.log(str1); //happy hahaily
后向查找
后向查找(lookbehind)是通过指定一个子表达式,然后从符合这个子表达式的位置出发开始查找符合规则的字串。
如:apple people 这两个单词,我只想匹配apple的 ple,如何实现呢?
/(?<=app)ple/
其实其原理就是与前向相反,还是看那个例子:
let str = 'happy syppy'
let str1 = str.replace(/(?<=sy)ppy/, 'haha')
console.log(str1); //happy syhaha
这里记住最关键的点: 括号里是条件,括号外才是要匹配和替换的内容!
6、逻辑处理
所谓的逻辑处理,就是说三种逻辑关系,与 或 非。
其中只讨论或 与 非的关系。
或关系,通常给子表达式进行归类使用。比如,我同时匹配a,b两种情况就可以使用(a|b)这样的子表达式。
而非关系,分为两种情况:一种是字符匹配,另一种是子表达式匹配。
1)字符匹配: 表示非需要使用^这个元字符。只有在[和]内部使用的^才表示非的关系。
2)子表达式匹配:非关系就要用到前面介绍的前向负查找子表达式(?!regex)或后向负查找子表达式(?
最后,只要掌握了上面六个主要规则,就能应对大多数用正则表达式实现的匹配和替换问题了。