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 星期三