RegExp
修饰符
i
: 不区分大小写g
: 全局匹配m
: 多行匹配s
: 使.
元字符匹配包括换行符在内的任意字符u
: 启用完整的 Unicode 匹配。用于处理 Unicode 字符,包括将 Unicode 字符视为独立的字符(即使它们是多个码点组成的)y
: 粘附匹配。要求从目标字符串的当前位置开始匹配,并且只匹配连续的字符 粘附匹配 demo:const pattern = /\d+/y;
const str = "123abc456def";
console.log(pattern.exec(str)); // 匹配 "123"
console.log(pattern.lastIndex); // 输出 3
console.log(pattern.exec(str)); // 匹配 "456"
console.log(pattern.lastIndex); // 输出 6
console.log(pattern.exec(str)); // null,因为没有连续的数字
console.log(pattern.lastIndex); // 输出 0,重置为 0
归类 / 或
[abc]
匹配 a 或 b 或 c[^ab]
匹配非 a 或非 b,而不是非 ab,注意边界量词(锚字符) 的^
区别[a-z0-9]
匹配小写字母 a 到 z、数字 0 到 9(x|y)
匹配 x 或 y
元字符
.
查找单个字符,除了换行和行结束符\w
查找单词字符\W
查找非单词字符\d
查找数字\D
查找非数字字符\s
查找空白字符\S
查找非空白字符\b
匹配单词边界\B
匹配非单词边界\0
查找 NUL 字符 ASCII 中与 0 对应的字符通常称为 NUL\n
查找换行符\f
查找换页符\r
查找回车符\t
查找制表符\v
查找垂直制表符\xxx
查找以八进制数 xxxx 规定的字符\xdd
查找以十六进制数 dd 规定的字符\uxxxx
查找以十六进制 xxxx 规定的 Unicode 字符
需要转义的字符(也属于元字符)
\
^
$
.
|
?
*
+
(
)
[
]
{
}
量词
{n,m}
n-m 次{n}
n 次{n,}
n-♾️ 次?
可选次 相当于{0,1}+
1-♾️ 次 相当于{1,}*
任意次 相当于{0,}(xyz)+
可联合捕获使用
边界量词(锚字符)
^
以什么开头$
以什么结尾
捕获分组与非捕获分组
(...)
捕获,例如/my name is (hyminghan)/g
(?:...)
非捕获
反向引用
使用捕获分组时,有分组的会顺序记录,可以用 $n
(表达式中用\n
) 语法去取值,大多数 JavaScript 引擎通常支持至少 99 个捕获组。意味着可以使用 $1
到 $99
来引用正则表达式中的捕获组
同时也可以自定义捕获组名:
- 声明语法:
(?<name>A)
- 正则表达式中反向引用语法:
\k<name>
- replace 方法中反向引用语法:
$<name>
ps: 反向引用可一起使用,例如:
/(?<name>A)\k<name>\1/.test("AAA"); // true
前瞻/后顾(也叫正则断言)
前瞻:
A(?=B)
查找 B 前面的 A
后顾:
(?<=B)A
查找 B 后面的 A
负前瞻:
A(?!B)
查找后面不是 B 的 A
负后顾:
(?<!B)A
查找前面不是 B 的 A
注意
正则断言在一些 IOS 系统存在兼容性问题
动态关键字
有时候需要动态关键字,而动态关键字只能通过 new RegExp
创建,而不能通过字面量方式创建,如下
const dynamicKey = key.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"); // 元字符字符转义
const reg = new RegExp(`obj\\.${dynamicKey}`, "g"); // ✅
const reg = /obj\.dynamicKey/g; // ❌
注意 new RegExp
和 字面量 创建的正则转义字符写法区别
正则小案例
千位分隔符
const formatNum = "10200300".replace(/\B(?=(?:\d{3})+(?!\d))/g, ",");
console.log(formatNum); // 10,200,300
大于 15 小于等于 50
const isGt15Le50 = /^1[6-9]$|^[2-4]\d$|^50$/.test("16");
console.log(isGt15Le50); // true
模拟 Vue 插值表达式解析
let data = {
name: "hyminghan",
hobby: ["guitar", "coding"],
};
let str = '你好,我叫{{name}},我喜欢{{hobby.join("、")}}';
str = str.replace(/\{\{([\d\D]+?)\}\}/g, (v, k) => {
with (data) {
return eval(k);
}
});
console.log(str); // 你好,我叫hyminghan,我喜欢guitar、coding
英文首字母自动大写
let str = "i like guitar and coding";
str = str.replace(/^\w|\s\w/g, (v) => v.toUpperCase());
console.log(str); // I Like Guitar And Coding
颜色格式转换
function rgb2hex(sRGB) {
if (!/^rgb\((\d{1,3},\s*){2}\d{1,3}\)$/.test(sRGB))
return "请输入正确的rgb颜色代码";
let color = "#";
sRGB.replace(/\d+/g, (n) => (color += ("0" + (+n).toString(16)).slice(-2)));
return color;
}
const color = rgb2hex("rgb(255, 100, 0)");
console.log(color); // #ff6400
简单 URL 参数解析
let url = "https://www.baidu.com?user=admin&password=123#haha";
const params = {};
url.replace(/\??(\w+)=(\w+)&?/g, function (a, k, v) {
params[k] = v;
});
console.log(params); // { user: 'admin', password: '123' }
CSS 样式转大驼峰
function cssStyle2DomStyle(sName) {
return sName.replace(/-[a-z]/g, (v, i) =>
i === 0 ? v.slice(-1) : v.slice(-1).toUpperCase()
);
}
const style1 = cssStyle2DomStyle("border-radius");
const style2 = cssStyle2DomStyle("-webkit-border-image");
console.log(style1); // borderRadius
console.log(style2); // webkitBorderImage
时间戳格式化小工具
function formatDate(t, str) {
t = new Date(t);
const obj = {
yyyy: t.getFullYear(),
yy: ("" + t.getFullYear()).slice(-2),
M: t.getMonth() + 1,
MM: ("0" + (t.getMonth() + 1)).slice(-2),
d: t.getDate(),
dd: ("0" + t.getDate()).slice(-2),
H: t.getHours(),
HH: ("0" + t.getHours()).slice(-2),
h: t.getHours() % 12,
hh: ("0" + (t.getHours() % 12)).slice(-2),
m: t.getMinutes(),
mm: ("0" + t.getMinutes()).slice(-2),
s: t.getSeconds(),
ss: ("0" + t.getSeconds()).slice(-2),
w: ["日", "一", "二", "三", "四", "五", "六"][t.getDay()],
};
const reg = /y{2,4}|m{1,2}|d{1,2}|h{1,2}|s{1,2}|w/gi;
return str.replace(reg, (k) => obj[k]);
}
const date1 = formatDate(1627430400000, "yyyy-MM-dd HH:mm:ss 星期w");
const date2 = formatDate(new Date(1627430400000), "yy-M-d H:m:s 星期w");
console.log(date1); // 2021-07-28 08:00:00 星期三
console.log(date2); // 21-7-28 8:0:0 星期三