mirror of
https://github.com/pengzhanbo/vuepress-theme-plume.git
synced 2026-04-23 10:58:13 +08:00
266 lines
12 KiB
Markdown
266 lines
12 KiB
Markdown
---
|
||
title: 正则表达式
|
||
createTime: 2022/03/08 06:32:01
|
||
permalink: /post/tjya08e9
|
||
author: pengzhanbo
|
||
top: false
|
||
type: # original: 原创: reprint 转载 可为空不填
|
||
tags:
|
||
- javascript
|
||
---
|
||
|
||
本文正则表达式基于`javascript`,不同的计算机语言对正则表达式的支持情况以及实现,语法不尽相同,不一定适用于其他语言。
|
||
|
||
<!-- more -->
|
||
|
||
### 简介
|
||
`正则表达式`,是一种文本模式(Regular Expression),是对字符串的一种匹配查找规则。可以方便的在某一文本字符串中,查找、定位、替换符合某种规则的字符串。
|
||
|
||
比如说,我们想要找出一段文本中的手机号码,文本内容如下:
|
||
``` html
|
||
name:Mark tel:13800138000
|
||
name:Jhon tel:13800138888
|
||
```
|
||
很明显,在这段文本中,手机号码是以 `tel:`开头,这符合一定的规则,这样我们可以通过正则表达式来书写这个规则,然后去查找匹配:
|
||
``` js
|
||
var text = `name:Mark tel:13800138000
|
||
name:Jhon tel:13800138888`;
|
||
|
||
var result = text.match(/tel:(1\d{10})/);
|
||
// ["tel:13800138000", "13800138000", index: 0, input: "tel:13800138000", groups: undefined]
|
||
var tel = result[1];
|
||
// 13800138000
|
||
```
|
||
`/tel:(1\d{10})/` 便是所说的正则表达式。
|
||
|
||
### `RegExp`与字面量
|
||
在`javascript`中,我们可以使用构造函数`RegExp` 创建正则表达式。
|
||
|
||
`new RegExp(pattern[, flags])`
|
||
``` js
|
||
var regExp = new RegExp('\\d', 'g');
|
||
```
|
||
也可以通过 字面量的方式:
|
||
``` js
|
||
var regExp = /\d/g;
|
||
```
|
||
|
||
两种创建正则表达式适用的场景有些细微的不同,一般使用`new RegExp()`来创建动态的正则表达式,使用字面量创建静态的正则表达式。
|
||
|
||
正则表达式字面量是提供了对正则表达式的编译,当正则表达式保持不变时,用字面量的方式创建正则表达式可以获得更好的性能。
|
||
|
||
_以下讨论以正则表达式字面量来创建正则表达式_
|
||
|
||
`正则表达式`一般由`元字符`和普通字符组成。
|
||
|
||
### 元字符
|
||
元字符也叫特殊字符,是正则表达式规定的,对符合特定的单一的规则的字符的描述。
|
||
| 字符 | 含义 |
|
||
|:----:|-----|
|
||
| \\ |在非特殊字符的前面加反斜杠,表示这个字符是特殊的,不能从字面上解释。比如在`\d`描述的不是一个普通的字符`d`,而是正则表达式中的数值`0-9`。<br/>如果在特殊字符前面加反斜杠,这表示将这个字符转义为普通字符,比如`?`在正则中有其特殊含义,前面加反斜杠\?,这可以将其转为普通的`?`。|
|
||
|^|匹配文本开始的位置,如果开启了多行标志,也会匹配换行符后紧跟的位置。<br/>比如`^a`会匹配`abc`,但不会匹配到`bac`。|
|
||
|$|匹配文本结束的位置,如果开启了多行标志,也会匹配换行符前紧跟的位置。<br/>比如`b$`会匹配`acb`,但不会匹配到`abc`。|
|
||
|*|匹配前一个表达式0次到多次。<br/>比如,`ab*`会匹配到`abbbbbbc`中的`abbbbbb`,以及`acbbbbb`中的`a`。|
|
||
|+|匹配前一个表达式1次到多次。<br/>比如,`ab+`会匹配到`abbbbbbc`中的`abbbbbb`,但不会匹配`acbbbbb`。|
|
||
|?|匹配前一个表达式0次到1次。<br/>比如,`ab*`会匹配到`abbbbbbc`中的`ab`,以及`acbbbbb`中的`a`。|
|
||
|.| 匹配除换行符之外的任何单个字符。|
|
||
|x\|y| 匹配 x或者y。|
|
||
|\[xyz\]| 表示一个字符的集合。匹配集合中的任意一个字符。可以使用破折号`-`来指定一个字符范围。<br/>比如,`[0-4]`和`[01234]`,都可以匹配`4567`中的`4`。|
|
||
|\[^xyz\]| 表示一个方向字符集合。匹配任意一个不包括在集合中的字符。可以使用破折号`-`来指定一个字符范围。<br/>比如,`[0-4]`和`[01234]`,都可以匹配`2345`中的`5`。|
|
||
|{n}|n为一个整数,表示匹配前一个匹配项n次。<br/>比如`a{2}`不会匹配`abc`中的`a`,但会匹配`aaaabc`中的`aa`。|
|
||
|{m,n}| m,n都是一个整数,匹配前一个匹配项至少发生了m次,最多发生了n次。<br/>当m,n值为0时,这个值被忽略,当n值不写,如`{1,}`表示1次到多次。当m值不写时,如`{,1}`表示0次到1次。|
|
||
|(x)|匹配`x`并且捕获该匹配项。称为捕获括号,括号中的匹配项也称作子表达式。|
|
||
|(?:x)| 匹配`x`但不捕获该匹配项。称为非捕获括号。|
|
||
|x(?=y)| 匹配`x`且当`x`后面跟着`y`。称为正向肯定查找(正向前瞻)。|
|
||
|x(?!y)| 匹配`x`且当`x`后面不跟着`y`。称为正向否定查找(负向前瞻)。|
|
||
|[\b]|匹配一个退格(U+0008)。|
|
||
|\b |匹配一个词的边界。匹配的值的边界并不包含在匹配的内容中。|
|
||
|\B|匹配一个非单词的边界。|
|
||
|\d|匹配一个数字。等价于`[0-9]`。|
|
||
|\D|匹配一个非数字。等价于`[^0-9]`。|
|
||
|\n|匹配一个换行符 (U+000A)。|
|
||
|\r|匹配一个回车符 (U+000D)。|
|
||
|\s|匹配一个空白字符,包括空格、制表符、换页符和换行符。<br/>等价于`[ \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]`|
|
||
|\S|匹配一个非空白字符。|
|
||
|\t|匹配一个水平制表符 (U+0009)。|
|
||
|\w|匹配一个单字字符(字母、数字或者下划线)。<br/> 等价于`[A-Za-z0-9_]`。|
|
||
|\W|匹配一个非单字字符。|
|
||
|\xhh|与代码 hh 匹配字符(两个十六进制数字)|
|
||
|\uhhhh|与代码 hhhh 匹配字符(四个十六进制数字)。|
|
||
|
||
|
||
上表在多数文章都会提及,但有一些注意的细节,下面我单独拎出来说说。
|
||
|
||
1. __`[xyz]` 匹配集合中的任意一个字符__ <br/>
|
||
这个字符集的元素,可以是普通字符,也可以是特殊字符,也可以用破折号`-`规定一个字符集范围。<br/>
|
||
以匹配数字为例,可以写成`[0123456789]` ,也可以写成`[\d]`,也可以写成`[0-9]`。<br/>
|
||
类似于`()`等特殊字符,在`[]`中有其作用,都特殊字符的作用一致,不能直接当做普通字符来使用,所以我们需要使用反斜杠`\`将其转义为普通字符,值得注意的是,上表的特殊字符中,星号`*`、小数点`.`在`[]`中并没有特殊用途,所以不需要做转义处理,当然,即使做了转义,也不会出现问题;而破折号`-`在`[]`中有其特殊作用,所以作为普通字符使用时,需要转义。
|
||
|
||
2. __`?`:匹配前一个表达式0次到1次。__<br/>
|
||
其实这里准确描述来说,匹配前一个表达式,且该表达式 __非任何量词 `*`、 `+`、`?` 或 `{}`__ ,匹配前一个表达式0次到1次。<br/>
|
||
如果紧跟在 __非任何量词 `*`、 `+`、`?` 或 `{}`__ 的后面,将会使量词变为非贪婪的(匹配尽量少的字符)<br/>
|
||
_贪婪与非贪婪匹配,我们在下文细说。_
|
||
|
||
### 等价字符
|
||
|
||
正则表达式中,有不少特殊字符的写法,是等价的,也可以说是简写形式,下表的左右两边,都是等价的。
|
||
|regExp|regExp|
|
||
|--|--|
|
||
|*|{0,}|
|
||
|+|{1,}|
|
||
|?|{0,1}|
|
||
|\d|\[0-9\]|
|
||
|\D|\[^0-9\]|
|
||
|\w|\[a-zA-Z_\]|
|
||
|\W|\[^a-zA-Z_\]|
|
||
|\s|\[ \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff\]|
|
||
|
||
### 贪婪模式与非贪婪
|
||
__什么是贪婪模式?__
|
||
|
||
贪婪是指正则表达式匹配时,是贪心的,会尽可能的匹配多的字符,主要体现在`量词特殊字符`:
|
||
``` js
|
||
// 匹配一个到多个数字
|
||
var r = /\d+/;
|
||
var t1 = '12a';
|
||
var t2 = '1234a';
|
||
var t3 = 'a12b345';
|
||
console.log(t1.match(r)[0]); // 12
|
||
console.log(t2.match(r)[0]); // 1234
|
||
console.log(t3.match(r)[0]); // 12
|
||
```
|
||
`非贪婪`,即是让正则表达式匹配尽量少的字符。那么如何改变正则表达式的贪婪模式?
|
||
|
||
__在量词特殊字符后面紧跟使用`?`__
|
||
|
||
我们说说的量词包括`*`, `+`, `?`, `{m,n}`。那么紧跟了`?`,会有什么不同的表现呢?
|
||
|
||
我们从例子来分析:
|
||
``` js
|
||
var r1 = /<div>.*<\/div>/;
|
||
var r2 = /<div>.*?<\/div>/;
|
||
var str = '<div>aaa</div>bbb<div></div>ccc';
|
||
```
|
||
变量`r1`是贪婪匹配,得到的结果会是什么呢?
|
||
``` js
|
||
console.log(str.match(r1)[0]);
|
||
// <div>aaa</div>bbb<div></div>
|
||
```
|
||
在这段字符串中,有两个`</div>`的匹配字符串,正则表达式在遇到第一个`</div>`匹配字符项时,同时满足了`/.*/`和`/<\/div>/`的匹配条件,优先作为`/.*/`的匹配值,在遇到第二个时,同样还是优先作为`/.*/`的匹配值,直到匹配的字符串`str`的结束,没有满足条件的匹配字符串,再把第二个`</div>`作为`/<\/div>/`的匹配值。最终得到了`<div>aaa</div>bbb<div></div>`的匹配结果。
|
||
|
||
变量`r2`这是非贪婪匹配,得到的结果又会有所不同:
|
||
``` js
|
||
console.log(str.match(r1)[0]);
|
||
// <div>aaa</div>
|
||
```
|
||
同样,两个`</div>`的匹配字符串,但实际非贪婪匹配模式,在匹配到第一个`</div>`,就不会再继续向下匹配字符串了。
|
||
|
||
__也就是说,贪婪匹配是,在满足规则下,尽可能多的匹配更多的字符串,直到字符串结束或没有满足规则的字符串了;非贪婪匹配是,在满足规则下,尽可能少的匹配最少的字符串,一旦得到满足规则的字符串,就不再向下匹配。__
|
||
|
||
1. `x*?`:尽可能少的匹配`x`,匹配的结果可以是0个`x`;
|
||
2. `x+?`:尽可能少的匹配`x`,但匹配的结果至少有1个`x`;
|
||
3. `x??`:尽可能少的匹配`x`, 匹配的结果可以是0个`x`,但最多可以有一个`x`;
|
||
4. `x{m,n}?`:尽可能少的匹配`x`,但匹配的结果至少有m个`x`,最多可以有n个`x`;
|
||
|
||
可能从字面来说,不好理解 `x??`, `x{m,n}?` ,来看一个例子就可以明白了:
|
||
``` js
|
||
var s1 = '<div>aa</div>';
|
||
var s2 = '<div>a</div>';
|
||
var s3 = '<div></div>';
|
||
var r1 = /<div>a??<\/div>/;
|
||
|
||
console.log(r1.test(s1)); // false
|
||
console.log(r1.test(s2)); // true
|
||
console.log(r1.test(s3)); // true
|
||
```
|
||
``` js
|
||
var s1 = '<div>aaa</div>';
|
||
var s2 = '<div>aa</div>';
|
||
var s3 = '<div>aaaa</div>'
|
||
var r1 = /<div>a{2,3}?<\/div>/;
|
||
var r2 = /<div>a{2,3}?/;
|
||
|
||
console.log(r1.test(s1)); // true
|
||
console.log(r1.test(s2)); // true
|
||
console.log(r1.test(s3)); // false
|
||
|
||
console.log(s1.match(r2)[0]); // <div>aa
|
||
console.log(s2.match(r2)[0]); // <div>aa
|
||
console.log(s3.match(r2)[0]); // <div>aa
|
||
```
|
||
|
||
### 正则表达式标志
|
||
|标志|描述|
|
||
|:--:| -- |
|
||
|g|全局搜索|
|
||
|i|不区分大小写搜索|
|
||
|m|多行搜索|
|
||
|y|执行“粘性”搜索,匹配从目标字符串的当前位置开始|
|
||
|u|Unicode模式。用来正确处理大于 \uFFFF 的Unicode字符|
|
||
|
||
__`m`__
|
||
|
||
使用`m`标志时,会改变开始(`^`)和结束字符(`$`)的工作模式,变为在多行上匹配,分别匹配每一行的开始和结束,即`\n`或`\r` 分割。
|
||
|
||
__`y`__
|
||
|
||
使用`y`标志时,匹配是从`RegExp.lastIndex`指定的位置开始匹配,匹配为真时,会修改`lastIndex`的值到当前匹配字符串后的位置,下次匹配从这个位置开始匹配,如果匹配为假时,不会修改`lastIndex`的值。
|
||
``` js
|
||
let reg = /o/y;
|
||
let str= 'foo';
|
||
|
||
// lastIndex 为 0,从字符 f 开始匹配
|
||
reg.test(str); // false
|
||
// 由于结果为 false, lastIndex 还是为 0
|
||
reg.test(str); // false
|
||
|
||
let str2 = 'oof';
|
||
// lastIndex 为 0 ,从字符 o 开始匹配
|
||
reg.test(str2); // true
|
||
// lastIndex 此时修改为 1, 从第二个 o 开始匹配
|
||
reg.test(str2); // true
|
||
// lastIndex 此时修改为 2
|
||
reg.test(str2); // false 此时开始匹配的字符是 f
|
||
// lastIndex没有被修改
|
||
reg.test(str2); // false
|
||
```
|
||
|
||
|
||
### 正则表达式中的捕获—— \1,\2,\3... 以及 $1,$2,$3...
|
||
|
||
在上文中我们介绍了 `(x)` 是匹配 `x` 并捕获,那么有了捕获就必然可以去使用捕获到的结果, `\1,\2,\3...` 以及` $1,$2,$3...` 便是指捕获的结果。
|
||
|
||
`\1, \2, \3, \4, \5, \6, \7, \8, \9` 在正则表达式中使用,捕获结果为正则表达式的源模式.
|
||
|
||
在这个正则表达式中`(bc)`被捕获并标记为`\1`, `(ef)`被捕获并标记为`\2`。
|
||
``` js
|
||
let reg = /a(bc)d(ef)/
|
||
```
|
||
也可以使用来简化正则表达式
|
||
``` js
|
||
let reg = /a(bc)dbc/
|
||
let reg2 = /a(bc)d\1/
|
||
|
||
let str = 'abcdbc';
|
||
|
||
reg.test(str); // true
|
||
reg2.test(str); // true
|
||
```
|
||
|
||
`$1, $2, $3, $4, $5, $6, $7, $8, $9` 是`RegExp`的包含括号子表达式的正则表达式静态的只读属性。
|
||
``` js
|
||
let reg = /a(bc)d/;
|
||
let str = 'abcd';
|
||
|
||
reg.test(str);
|
||
console.log(RegExp.$1); // bc
|
||
```
|
||
在 `String.replace()` 中使用:
|
||
``` js
|
||
let reg = /(\w+)\s(\w+)/;
|
||
let str = 'apple pear';
|
||
str.replace(reg, '$2 $1'); // pear apple
|
||
RegExp.$1; // apple
|
||
RegExp.$2; // pear
|
||
```
|