两种声明方式
*:字面量&&构造函数
const alphabet = '[a-z]';
const reg = new RegExp(`${alphabet}\\d+${alphabet}`, 'i'); //字面量
const reg = /[a-z]\d+[a-z]/i;//字面量
字面量:不易读
构造函数:二次转义会出现问题(需要检查),子内容可以重复使用
const reg = new RegExp(`\d+`);
reg.test('1'); // false
reg.test('ddd'); // true
常用方法
RegExp.prototype.test():输入必须是字符串(隐式转换也可以)
const reg = /[a-z]\d+[a-z]/i;
reg.test('a1a'); // true
reg.test('1a1'); // false
reg.test(Symbol('a1a')); // TypeError
RegExp.prototype.source 和 RegExp.prototype.flags
.source属性,返回两个斜杠之间的内容
.flags返回当前正则表达式的修饰字符串,第二个斜杠外面的内容
RegExp.prototype.exec() 和 String.prototype.match()
关于输入
这里有个转换问题:
RegExp.prototype.exec 要求输入字符串,遇到非字符串类型会尝试转换
String.prototype.match 要求输入正则表达式,遇到其它类型会先尝试转成字符串,再以字符串为 source 创建正则表达式
关于输出
当正则表达式含有 g 修饰符时,RegExp.prototype.exec 每次只返回一个匹配结果,数据格式和不含 g 修饰符相同。
String.prototype.match 会返回所有的匹配结果,数据格式会变为字符串数组。
由于 String.prototype.match 返回的数据格式不固定,因此大多数情况都建议使用 RegExp.prototype.exec
//reg.exec(string)
//string.match(exec)
const reg = /[a-z]\d+[a-z]/i;
reg.exec('a1a'); // ["a1a", index: 0, input: "a1a", groups: undefined]
reg.exec('1a1'); // null
'a1a'.match(reg); // ["a1a", index: 0, input: "a1a", groups: undefined]
'1a1'.match(reg); // null
RegExp.prototype.lastIndex
当前正则表达式最后一次匹配成功的结束位置(也就是下一次匹配的开始位置)
const reg = /(a)/g;
const str = 'a1a';
reg.lastIndex; // 0
reg.exec('a1a'); // ["a", "a", index: 0, input: "a1a", groups: undefined]
reg.lastIndex; // 1
reg.exec('a1a'); // ["a", "a", index: 2, input: "a1a", groups: undefined]
reg.lastIndex; // 3
reg.exec('a1a'); // null
reg.lastIndex; // 0
注意: lastIndex 不会自己重置,只有当上一次匹配失败才会重置为 0 ,因此,当你需要反复使用同一个正则表达式的时候,请在每次匹配新的字符串之前重置 lastIndex!
String.prototype.replace()、String.prototype.search()、String.prototype.split()
'a1a'.replace(/a/, 'b'); // 'b1a'
'a1a'.replace(/a/g, 'b'); // 'b1b'
'a1a'.search(/a/); // 0
'a1a'.search(/a/g); // 0
'a1a'.split(/a/); // ["", "1", ""]
'a1a'.split(/a/g); // ["", "1", ""]
正则表达式符号问题
/[0-9]+/:表示0-9之间的数字匹配一个或多个
问题:不一定准确
存在误判,如 /[0-9]+/.test(‘a1’) === true
/[0-9]+/.test('a1') === true //为什么会误判
/^\d+$/: ^后跟着起始字符,$前跟着结束字符
(Ps:我在markdown里打上面的表达式美元符号也得转义)
Attention:不能匹配带符号的数值,如 +1,-2,不能匹配小数,如 3.14159
/^[+-]?\d+(.\d+)?$/:匹配小数(markdown转义也要=_=)
()圆括号内是一个子表达式,当圆括号不带任何修饰符时,表示同时创建一个捕获组
? 在正则中有多种含义,作为限定符时,表示匹配零到一个
. 可以匹配除换行符之外的任意字符,当结合 s 修饰符时,可以匹配包括换行符在内的任意字符,当匹配小数点字符时需要转义
不能匹配无整数部分的小数,如 .123捕获组会带来额外的开销
/^[+-]?(?:\d.)?\d+$/*
(?: ):创建一个非捕获组
不能匹配无小数部分的数值,如 2.
不能匹配科学计数法,如 1e2、3e-1、-2.e+4
创建一个非捕获组
完整的正则
1. 完整的数值token
/^[+-]?(?:\d+\.?|\d*\.\d+)(?: e[+-]?\d+)?$/i
|: 用来创建分支,当位于圆括号内时,表示子表达式的分支条件,当位于圆括号外时,表示整个正则表达式的分支条件
i 修饰符
表示匹配时忽略大小写,在这个例子中用于匹配科学计数法的 e,去掉 i 修饰符需要把 e 改为 [eE]
思考题:这个正则已经没有缺点了吗?
const reg = /[+-]?(?:\d*\.)?\d+(?:e[+-]?\d+)?(?=px|\s|$)/gi;
function execNumberList(str) {
reg.lastIndex = 0;
let exec = reg.exec(str);
const result = [];
while (exec) {
result.push(parseFloat(exec[0]));
exec = reg.exec(str);
}
return result;
}
console.log(execNumberList('1.0px .2px -3px +4e1px')); // [1, 0.2, -3, 40]
console.log(execNumberList('+1.0px -0.2px 3e-1px')); // [1, -0.2, 0.3]
console.log(execNumberList('1px 0')); // [1, 0]
console.log(execNumberList('-1e+1px')); // [-10]
修饰符g,表示全局匹配,用于取出目标字符串中所有符合条件的结果
需要注意的点
- 按照 CSS 规范,只有数值为 0 才可以省略单位,这种情况没有必要靠正则来过滤
- 这个例子中只验证了 px 单位,实际还存在 pt、em、vw 等单位,并且没有考虑百分比的情况
- 实际工作中,要根据需求追加处理逻辑
2. 数值转货币
const reg = /(\d)(?=(\d{3})+(,|$))/g;
function formatCurrency(str) {
return str.replace(reg, '$1,');
}
console.log(formatCurrency('1')); // 1
console.log(formatCurrency('123')); // 123
console.log(formatCurrency('12345678')); // 12,345,678
{n}
限定符,表示重复 n 次,n 必须是非负整数
类似的语法还有:
{n, m} 表示重复 n 到 m 次,n 和 m 都必须是非负整数,且 n <= m
{n,} 表示重复 n 次以上
const reg = /\d(?=(?:\d{3})+(?:,|$))/g;
function formatCurrency(str) {
return str.replace(reg, '$&,');
}
const reg = /(?<=\d)(?=(?:\d{3})+(?:,|$))/g;
function formatCurrency(str) {
return str.replace(reg, ',');
}
环视中的圆括号也会生成捕获组,所以都要采用 (?: ) 的非捕获组形式
数值转货币格式
const reg = /(\d)(?=(\d{3})+(,|$))/g;
function formatCurrency(str) {
return str.replace(reg, '$1,');
}
console.log(formatCurrency('1')); // 1
console.log(formatCurrency('123')); // 123
console.log(formatCurrency('12345678')); // 12,345,678
$&可以表示本次完整匹配
所以上述例子中,$&和$1是一样的。