【自留】常用正则一览

反反复复

语法速查

1
2
3
  /    pattern   /  flags
   ↑       ↑          ↑
 起始符  匹配规则    修饰标志

标志

1
2
3
4
5
6
i // 不区分大小写
g // 搜索全部匹配项
m // 多行模式
u // Unicode
y // 必须从 lastIndex 处开始匹配,否则失败
s // 允许点 . 匹配换行符 \n

匹配规则

字符类是集合类的缩写

  • \d —— 和 [0-9] 相同,
  • \w —— 和 [a-zA-Z0-9_]相同,
  • \s —— 和 [\t \n \v \f \r ] 外加少量罕见的 Unicode 空格字符相同。
  • . ——匹配除换行符 (\n, \r, \u2028, \u2029) 之外的任意单个字符。
符号 名称 解释
[…] 集合 搜索给定字符中的任意一个 [a-f] 表示从 a 到 f 范围内的字符
[^…] 排除范围 匹配所有 除了给定的字符 之外的任意字符
{n 具体数量(量词) \d{5} \d{3,5}查3到5位 \d{3,} 查大于等于 3
+ >1(量词) 与 {1,} 相同
? <=1(量词) 与 {0,1} 相同
? 惰性模式 跟在量词后生效,/".+?"/g 将匹配的模式从贪婪转为惰性
* 任意数量(量词) 与 {0,} 相同
(…) 捕获组 go+ 匹配goooo 或 goooooooo  (go)+ 匹配 go,gogo,gogogo
\N 引用前面第 N 个捕获组的内容 \1找到第一个 (X) 并记住其内容。
| 选择 html|php|java(script)?

断言

用来判断某个位置前后是否满足特定条件,匹配到的内容并不包含这些断言本身

边界断言:

断言 名称 示例
^ 确定行首 /^abc/m 可匹配每一行开头的 abc
$ 确定行尾 /abc$/m 匹配行末的 abc
\b 单词边界(word boundary) /\bcat\b/ 匹配独立的单词“cat”
\B 非单词边界 /\Bcat\B/ 不匹配独立的单词,只匹配内部“cat”

还有一种断言用于判断一个位置的前后文本是否满足条件,感觉结合用例方法来看更好,放下面match里了。

字符串实例方法

match

match对“捕获组”(capturing groups)有两种完全不同的表现

没有g时:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const m = '2025-10-09'.match(/(\d{4})-(\d{2})-(\d{2})/)
/*
m 是一个类数组对象:
m[0] === '2025-10-09'   // 整体匹配
m[1] === '2025'         // 第1个捕获组
m[2] === '10'           // 第2个捕获组
m[3] === '09'           // 第3个捕获组
m.index   // 匹配起始下标
m.input   // 原字符串
m.groups  // 若使用了“命名捕获组”,这里是个对象
*/

有 g:返回“所有整体匹配”,不包含捕获组

1
2
'2025-10-09'.match(/\d{2}/g) // ['20','25','10','09']
'2025-10-09'.match(/(\d{4})-(\d{2})-(\d{2})/g) // ['2025-10-09']  ← 只有整体匹配,没有 m[1]/m[2]/m[3]

matchAll 有“全局匹配”和“捕获组”

1
2
3
4
const it = 'a1b22c333'.matchAll(/([a-z])(\d+)/g)
for (const m of it) {
  console.log(m[0], m[1], m[2]) // 整体、组1、组2
}
matchall结果展示

配合先行/后行断言使用

  • 肯定前瞻 (?=X):当前位置后面必须是 X
  • 否定前瞻 (?!X):当前位置后面不能是 X
  • 肯定后顾 (?<=X):当前位置前面必须是 X
  • 否定后顾 (?<!X):当前位置前面不能是 X
1
2
3
4
5
6
7
8
// 只匹配“foo 后面是 bar”的 foo
"foo bar foo baz".match(/foo(?=\sbar)/g); // ["foo"]
// 只匹配“foo 后面不是 bar”的 foo
"foo bar foo baz".match(/foo(?!\sbar)/g); // ["foo"](第二个)
// 只匹配“前面是 $ 的数字”(剥离货币符号)
"$123 and 456".match(/(?<=\$)\d+/g); // ["123"]
// 只匹配“前面不是字母的数字”
"a123 456".match(/(?<![A-Za-z])\d+/g); // ["456"]

后顾必须是“固定长度” 断言匹配到的内容并不包含这些断言本身,方便与 replace / split 结合(“在……前/后”插入/切分)使用

replace

1
2
3
4
5
6
7
let str = "We will, we will rock you"
const out1 = str.replace(/we/i, "$&...")
console.log(out1)
// We... will, we will rock you
const out2 = str.replace(/we/ig, "$&...")
console.log(out2)
// We... will, we... will rock you

更通用的方案是对replace的第二个参数使用回调,配合捕获组

replace(regexp, (match, p1, p2, …, offset, string, groups) => string)

  • match:整段匹配
  • p1/p2…:对应捕获组
  • offset:匹配在原串中的起始下标
  • string:原始字符串
  • groups:命名分组((?<name>…))对象

用命名分组重排日期

1
2
3
4
5
const s = "Due: 2025-10-09";
const out = s.replace(/(\d{4})-(\d{2})-(\d{2})/, (match, y, m, d) => {
  return `${d}/${m}/${y}`;
});
console.log(out); // "Due: 09/10/2025"

十六进制颜色转为 rgb

1
2
3
4
5
6
7
8
9
const s = "Primary: #1e90ff; Shadow: #000;";
const out = s.replace(/#([0-9a-f]{3}|[0-9a-f]{6})\b/gi, (m, hex) => {
  if (hex.length === 3) hex = hex.split("").map(x => x + x).join("");
  const r = parseInt(hex.slice(0,2), 16);
  const g = parseInt(hex.slice(2,4), 16);
  const b = parseInt(hex.slice(4,6), 16);
  return `rgb(${r}, ${g}, ${b})`;
});
console.log(out); // "Primary: rgb(30, 144, 255); Shadow: rgb(0, 0, 0);"

额外: 十进制值转其他进制(以字符串形式表示),最方便就是toString()

1
2
3
const n = parseInt("101010", 2); // 42  字符串(某进制) -> 十进制数
(42).toString(2)    // "101010"
(42).toString(8)    // "52"

替换独立词并做词典映射

1
2
3
4
5
6
7
8
const dict = { js: "JavaScript", css: "Cascading Style Sheets" };
const s = "I like JS, css and JSON (not js-on).";
const out = s.replace(/\b(js|css)\b/gi, (m) => {
  const v = dict[m.toLowerCase()];
  return v ?? m; // 映射不到就原样返回
});
console.log(out);
// "I like JavaScript, Cascading Style Sheets and JSON (not js-on)."

split

注意事项: g 标志对 split 没意义:split 会自己反复匹配分隔符 捕获组会进结果:想保留分隔符就用 (),不想保留就别加或者用非捕获组(?:X)

按任意空白切割

1
2
"  foo\tbar   baz ".trim().split(/\s+/);
// ["foo", "bar", "baz"]

保留分隔符

1
2
"1,2;3|4".split(/([,;|])/);
// ["1", ",", "2", ";", "3", "|", "4"]

用limit控制切割次数

1
2
3
4
5
"a,b,c,d".split(/,/, 2);
// ["a", "b"]

"2025-10-09".split(/-/, 2);
// ["2025", "10"]   // 第3段被丢弃

正则表达式方法

test

比如,用来判断单个字符是否在某个范围

1
2
3
4
/^[a-z]$/i.test(ch)      // 英文字母(大小写,i 不区分大小写)
/^[0-9]$/.test(ch)       // 数字
/^[A-F0-9]$/.test(ch)    // 十六进制字符(大写)
/^[a-z]+$/i.test(str)    // 整个字符串是否只由这些字符组成

exec

返回一次详细的匹配结果对象或 null 当正则带有 g 标志时,exec() 会记住上次匹配的位置lastIndex,
从而可以循环执行多次,逐步提取所有匹配

复习资料

练习用:

RegexOne 中文 regex101

发表了9篇文章 · 总计27.20k字
Built with Hugo
主题 StackJimmy 设计