正则表达式进阶

贪婪匹配 和 惰性匹配

  • 贪婪匹配是尽可能匹配更多的字符
  • 惰性匹配是尽可能匹配更少的字符

惰性匹配是在 * , + , {m,} 后加上 ?

  • *: 匹配前面 0 次或以上,尽可能多
  • +: 匹配前面 1 次或以上,尽可能多
  • {m,}: 匹配前面 m 次或以上,尽可能多
  • *?: 匹配前面 0 次或以上,尽可能少
  • +?: 匹配前面 1 次或以上,尽可能少
  • {m,}?: 匹配前面 m 次或以上,尽可能少

举例

字符串

1
1a-2b-3c-4d-e5-f6

除了 - 外,其他字符串都是不定长的,而且字符串也可以是其他除 - 外的字符甚至特殊符号

  • 贪婪匹配
    • 表达式:^.+\-
    • 匹配值:1a-2b-3c-4d-e5-
  • 惰性匹配
    • 表达式:^.+?\-
    • 匹配值:1a-

惰性匹配顺序问题

前面的例子,如果想匹配后面的 -f6,你可能会这样用

1
\-.+?$

但匹配结果是 -2b-3c-4d-e5-f6 而不是 -f6,和贪婪匹配结果一样

这是因为正则匹配是从前往后,当匹配到 -2 时发现匹配了一部分,就会继续向前查询 -2d > -2d- > -2d-3 > -2d-3c 等,直到查询 -2b-3c-4d-e5-f6 才找到满足条件的值

为了解决这个问题,可以用排除法,即排除前面的 -

1
\-[^\-]+$

字符串掐头去尾

在代码中,如果想去除字符串前面一部分,或者字符串后面一部分,可以用 正则 + 替换 的方式

文件名

1
image.png
  • 若只想要不带扩展名的名称,在 JS 中可以这样
1
\..+$
1
2
3
const file = "image.png"
const name = file.replace(/\..+$/, "");
console.log(name); // image
  • 如果文件名中可能包含多个 .
1
image.1.png

按上面的写法只能取到 image 而不是 image.1

这样做保留的文件名更完整

1
\.+[^\.]*$
1
2
3
const file = "image.1.png"
const name = file.replace(/\.+[^\.]*$/, "");
console.log(name); // image
  • 如果只想保留扩展名,可以这样
1
^.*\.
1
2
3
const file = "image.png"
const extended = file.replace(/^.*\./, "");
console.log(extended); // png

环视

也称为零宽度断言,环视可以根据某个模式之前或之后的内容,要求匹配其他模式

正前瞻

匹配且要求紧随其后的内容为分组匹配的内容

1
?=分组

[a-zA-Z](?=\d) ,若字母是数字则匹配该字母 ,否则匹配,即 [a-z] 必须匹配 \d

反前瞻

对正前瞻含义取反,即匹配且要求紧随其后的内容不为分组匹配的内容

1
?!分组

[a-zA-Z](?!\d) ,若字母是数字则匹配该字母 ,否则匹配,即 [a-z] 必须匹配 \d

正前顾

即对正前瞻方向取反,匹配且要求紧挨着之前的内容为分组匹配的内容

1
?<=分组

(?<=\d)[a-zA-Z] ,若字母是数字则匹配该字母 ,否则匹配,即 [a-z] 必须匹配 \d

反后顾

即对正前瞻方向取反,匹配且要求紧挨着之前的内容为分组匹配的内容

1
?<!分组

(?<!\d)[a-zA-Z] ,若字母是数字则匹配该字母 ,否则匹配,即 [a-z] 必须匹配 \d

正向引用

子匹配可以被引用,使用 \n 访问

abcd<custom-button>link</custom-button>efg 匹配 custom-button 标签和其中的内容

1
<(custom-button)>.*</\1>