正则表达式(五):括号与正则符号优先级

括号

作用

提供分组和分支结构

分组和分支结构是括号中的基础使用, 作用是提供一个子表达式

分组

使用括号进行分组, 用来表明括号内的所有字符是一个组合, 从而对于括号内的字符组合进行范围限定, 可以对于整个括号添加量词, 表明匹配括号内的元素进行整体量词限定:
1
2
3
let str = 'abababc ab';
let regex = /(ab)+/g;
str.match(regex); // Array(2) ["ababab", "ab"]

分支结构

我们在正则中使用 | 表明匹配 | 符号左边或者右边的正则, 使用括号包裹| 用来限定 | 的作用范围, 例如:
1
2
3
4
5
let str = 'ab ac';
let regex = /a(b|c)/g;
// 如果不加上括号 regex = /ab | c/g 那么匹配的是 ab 或者 c
// 加上括号表明分支结构只有 b 和 c
str.match(regex); // ['ab', 'ac']
在使用 match 的时候,添加全局标识符 g 加不加是不同的:上面的代码如果不用添加 g 标识符, 那么返回结果如下:不会进行全局匹配, 只会匹配到 ab 就会返回。
1
2
3
4
5
> let str = 'ab ac';
> let regex = /a(b|c)/;
> str.match(regex);
> // ['ab', 'b']
>

分组引用

在正则中使用括号进行包裹正则表达式时, 正则引擎会对于正则表达式进行分组,正则引擎会在匹配过程中, 给每一个分组都开辟一个内存空间, 用来存储每一个分组匹配到的数据。例如, 我们想要对于日期进行匹配:
1
2
3
let date = '2019-02-05';
let regex = /(\d{4}-(\d{2})-(\d{2}))/;
date.match(regex); // ["2018-02-02", "2018", "02", "02"]
这里使用 match 进行匹配的正则表达式中没有包含有标识符 g, 因此使用 match 与使用 exec 的返回结果是一样的。

符号的优先级

在正则表达式中, 使用操作符进行操作的时候, 需要规定操作符的优先级, 用来表示操作符是谁先操作, 谁后操作, 在正则表达式中, 各种操作符的优先级如下:从上到下:
描述符 操作符
转义符 \
括号与方括号 (...) (?:...) (?=...) (?!...) [...]
量词限定符 {m} {m, n} {m, } ? * +
位置和序列 ^ $ \元字符 一般字符
管道符 `
上面的操作符从上到下, 优先级从高到低;示例: /ab?(c|de*)+|fg/上面的操作过程如下:因为在正则中存在管道符 | 用于分割为 ab?(c|de*)+fg 两个部分, 先执行这两个部分:执行 ab?(c|de*)+: 在这个过程中,先执行括号内的正则, 然后执行字符 a, b ? 以及 +, 在括号内c|de* 的执行顺序中, 先执行 c, de*, 其中 * 是和 e 在一起的, 因此 * 限定的量词是 e;如下: 匹配一个字符串, 字符串中的字符为 a , b, c 中的一个, 并且, 字符串的长度为 3 的倍数;
1
let regexp = /([abc]{3})+/;
这里是将量词 3 使用括号进行包裹如下: 匹配一个 ipv4 的地址:IP4 的地址的结构为这种形式:3位数.3位数.3位数.3位数其中 3 位数的数字范围为 0 - 255, 因此, 对于一位数而言, 可以这样写:
1
/0{0,2}\d+/ // 匹配 000 - 009
对于两位数而言, 进行匹配:
1
/0?\d{2}/
对于三位数而言, 需要进行匹配:
1
/1\d{2}/ // 匹配 100 - 199
1
/2[0-4]\d/ // 匹配 200 - 249
1
/25[0-5]/ // 匹配 250 -255
上面的这几种匹配结构为或的关系, 因此, 上面几种结构使用管道符 | 进行连接:
1
(0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])
匹配. 号:
1
/^((0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])\.)(0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])$/
上面的也可以第二部分可以使用 \2 进行代替:
1
/^((0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])\.)\2$/