工作中每每用到正则表达式都是去搜索引擎里查,凭着零散的正则知识,勉强拼凑一个出来用。下次遇到同样的问题,还是不会,又去查,很低效的一种行为。
既然经常遇到那就,一次性把它学会。
# 什么是正则表达式?
首先正则表达式一种处理文字的工具,比如以下场景:
- 你正在编辑一段文本,想把文本中的所有
hello替换为hi,但是只是替换单词本身,不是将某个包含hello的字符串也进行替换,比如ihello就不做处理 - 搜索某个特定的文本,但是只是想找到出现在特定位置的,比如每行的开头或者结尾处
正则表达式是一般内置于某种软件(比如文字编辑工具)或者编程语言中
正则表达式,一般被称为模式,有两部分组成,普通字符 + 特殊字符(也称为元字符),两者组合可以构成很复杂的表达式。
# 匹配单个字符
# 匹配单个字符
最简单的正则表达式就是纯文本,比如下面这段文本
Hello,my name is Tao.
我想匹配其中的Tao,其正则表达式为/Tao/
在
js中正则表达式是同时也是一个正则表达式对象 ,创建一个正则表达式可使用字面量两个斜杠和其中的模式构成
# 匹配任意字符
上面匹配模式是已知的静态文本,假如需要匹配不可预知的字符呢?
在正则表达式中,英文字符. 用来匹配任意一个单个的字符,比如/hel.o/g可以在下面文本中找到三处匹配
hello hel-o hel2o
上面正则表达式末尾有标记
g,表示global,意为匹配多个结果,返回数组,没有该标记,返回匹配到的第一个结果
# 匹配一组字符
如果在cat cot cet这组单词中,找到 cat 和cot 这两个单词。上面的方法就有点显得捉襟见肘。/c[ao]t/,这个表达中用一对方括号表示一组字符集合,[ao]表示匹配到这个集合中的任意一项即可,可以理解为[和] ,我们会频繁使用到一些字符结合,比如 0~9,a~z 等,正则表达式提供了一个特殊的元字符-,可以用来表示一种字符区间,比如:[a-z],表示所有的小写字母。
# 取非匹配
某些情况,我们给出一组不需要得到的字符组, 这个时候相当于对现有字符组做取非操作,用元字符^表示,例如
[^0-9]表示匹配任何不是数字的字符
行文至此,我们已经接触了三个特殊字符,.、-、^,其实正则表达式中有很多特殊字符,这类字符统称为元字符,大部分元字符需要用\标记
| 元字符 | 说明 |
|---|---|
| \ | 将下一个字符标记为元字符 |
| \d | 匹配一个数字,等价于[0-9] |
| \D | 匹配一个非数字,等价于[^0-9] |
| \w | 匹配字母、数字、下划线,等价于[A-Za-z0-9_] |
| . | 除了换行符之外的任何字符 |
| \s | 空白字符串,包括空格,制表符、换行符、换页符 |
一般来说,大写的元字符是小写元字符的取非结果
有的时候,我们需要匹配元字符本身,则需要对元字符进行转义,如\这个元字符
文本:
file\doc
正则表达式:
file\\其中出现两侧斜杠,第一次表转义,下一个字符将作为普通字符处理
# 重复匹配
前面的都是针对单个字符的单词匹配,某些情况下,我们的正则表达式会重复使用多次,我们也可以将该表达式写多次,但是显然不是最好的方式,我们可以指定,对前面的正则表达式循环多少次,一般来说,可以分为,至少 n 次,n 可以是任何整数,也可以表示一个区间 n-m 次,至少 n 次,至多 m 次等。特殊点的情况,出现一次或者没有,用 ?表示,0 次,或者无数次,用*表示,出现一次或者无数次+,表格总结如下
| 元字符 | 说明 |
|---|---|
| ? | 出现一次或者没有 |
| * | 0次或者无数次 |
| + | 1次或者无数次 |
| {n,} | 至少 n次 |
| {n,m} | 至少n次,最多m次 |
{} 表区间,{n,m}形成一个左闭又闭的区间,{n,} 表示最少 n 次,右边无限制。
比如\w+@\w+\.\w+这个正则表达式可以匹配一个电子邮件地址
# 防止过度匹配
* 和 +都是所谓的贪婪型元字符,尽可能多的匹配,有时候我们不需要贪婪型模式,可以使用这些字符的懒惰型模式,很简单就是这些元字符后面加上 ?,表示尽可能的少匹配。
# 位置匹配
我们现在可以运用前述的各种元字符组合对文本任意字符串进行匹配,这些字符出现在任意位置,但是有的时候我们需要匹配特定位置的字符,则需要用到位置匹配
# 单词边界
比如字符串
this is an apple applele.
我想找到单词 apple ,很自然我们的正则是/apple/,可是这样还会找到 applele 中前面的 apple,正确的正则应该是/\bapple\b/
\b表示匹配一个单词的开始或者结尾,\B表示不是一个单词边界
# 字符串边界
^表示字符串的开头,$表示字符串末尾
# 子表达式
子表达式是整个表达式的一部分,把一个表达式划分成数个小的表达式,把小表达式当做一个独立的元素来使用,字表达式用(和)包裹起来。字表达嵌套,回溯引用可以创建出来很多高级复杂的正则表达式
# 分组
(Regex)表示分组,比如匹配 IP 地址 172.168.2.1,未使用分组的正则表达式是:
/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
如果使用分组,上述表达式可以简化为,/(\d{1,3}\.){3}\d{1,3}/
# 回溯引用
假设有一段文本,其中有一个单词连续输入两遍,你想找到第二个输入的单词,很明显,要想找到第二遍输入的这个单词,这个单词必须是已知的,回溯引用,允许正则表达式,引用前面匹配到的结果。
找到字符串连续两个相同的单词 /\b(\w+)\s\1/,经常用在替换操作中,js 中替换操作实例
const str = 'hello,my name name is jack'
str.replace(/\b(\w+)\s\1/g,'$1')//替换连续相同的两个单词为一个单词
# 前后查找
目前为止我们的正则表达式都是对整个表达式查找,有些时候,我们需要对字符串的某个位置前或者后进行查找。我们需要一个确定位置的表达式,匹配结果不返回,只是用来确定位置
# 向前查找
(?=Regex)开头得表达式表示向前查找,比如我想找到 URL 地址中协议名
http:// www. forta. com/ https:// mail. forta. com/ ftp:// ftp. forta. com/
正则表达式为.+(?=:),向前查找表达式(?=:),匹配 :后,并不返回结果,而是向前匹配.+
# 向后查找
(?<=Regex)是向后查找表达式的模式,比如 apple 和people 这两个单词都是以 le结尾的,想找到apple 中的 le该怎么办?可以通过app限制查找,/(?<=app)le/就可以找到了
向前向后查找其实是有结果的,只不过这个结果长度永远为零,所以有时候被称为零宽度(zero-width)操作。