js正则使用问题记录

Javascript 中可以通过以下两种方式写正则:

  1. 正则表达式字面量
  2. 通过构造函数 RegExp 的实例
    例如,创建一个正则用于精确匹配字符串 ‘test’。
let regExp = /test/

let regExp = new RegExp('test') 
// 或者
let regExp = new RegExp(/test/)

常见的修饰符如下:

模式说明
gglobal 简写,全局匹配,找到所有满足匹配的子串,而不是默认只匹配首次结果。
iignore case 简写,匹配过程中,忽略英文字母大小写,如 /test/i 可以匹配 Test、teSt、TesT 等。
mmultiline 简写,多行匹配,把 ^ 和 $ 变成行开头和行结尾。比如可以匹配文本域(textarea)元素中的值。
uunicode 简写,允许使用 Unicode 点转义符。
ysticky 简写,开启粘连匹配,正则表达式执行粘连匹配时试图从最后一个匹配位置开始。

RegExp 相关实例方法:

模式说明
1,…,1,…,1,…,9最近一次第 1-9 个分组捕获的数据。
input最近一次目标字符串,可以简写成 $_ 。
lastMatch最近一次匹配的文本,可以简写成 $& 。
lastParen最近一次捕获的文本,可以简写成 $+ 。
leftContext目标字符串中 lastMatch 之前的文本,可以简写成 $` 。
rightContext目标字符串中 lastMatch 之后的文本,可以简写成 $’ 。
匹配字符
模式说明
字母、数字匹配字符本身。比如/javascript/,匹配 “javascript”;/123/,匹配 “123”。
\0匹配 NUL 字符。
\t匹配水平制表符
\v匹配垂直制表符。
\n匹配换行符。
\r匹配回车符。
\f匹配换页符。
\xnn匹配拉丁字符。比如 \xOA 等价于 \n。
\uxxxx匹配 Unicode 字符。比如 \u2028 匹配行终止符,\u2029 匹配段终止符。
\cX匹配 ctrl+X。比如 \cI 匹配 ctrl+I,等价于 \t。
[\b]匹配 Backspace 键(特殊记忆)。
.通配符,匹配除了少数字符(\n)之外的任意字符。
\d匹配数字,等价于 [0-9]。
\D匹配非数字, 等价于 [^0-9]
\w匹配单词字符,等价于 [a-zA-Z0-9_]。
\W匹配非单词字符,等价于 [^a-zA-Z0-9_]。
\s匹配空白符,等价于 [ \t\v\n\r\f]。
\S匹配非空白符,等价于 [^ \t\v\n\r\f]。

量词

模式说明
{n,m}连续出现 n 到 m 次。贪婪模式。
{n,}至少连续出现 n 次。贪婪模式。
{n}连续出现 n 次。贪婪模式。
?等价于 {0,1}。贪婪模式。
+等价于 {1,}。贪婪模式。
*等价于 {0,}。贪婪模式。
{n,m}?连续出现 n 到 m 次。惰性模式。最多匹配到 n 个
{n,}?至少连续出现 n 次。惰性模式。最多匹配到 n 个
{n}?连续出现 n 次。惰性模式。
??等价于 {0,1}?。惰性模式。
+?等价于 {1,}?。惰性模式。
*?等价于 {0,}?。惰性模式。

贪婪模式――在匹配成功的前提下,尽可能多的去匹配
惰性模式――在匹配成功的前提下,尽可能少的去匹配

记录问题
1.设置全局标志,test() 的执行会改变正则表达式 lastIndex属性。连续的执行test()方法,后续的执行将会从 lastIndex 处开始匹配字符串,(exec() 同样改变正则本身的 lastIndex属性值)。

var regex = /en/g; 
undefined
regex.test("hsh_en")
true
regex.test("hsh_en")
false
regex.test("hsh_en")
true

正则表达式带有g标识进行全局匹配时,匹配成功后,regex实例中会有一个lastIndex属性去记录本次命中正则的最后一位的下标+1,用于在下一次调用test的时候,从lastIndex开始进行匹配。
问题原因: 正则实例保存,反复使用,导致lastIndex值未重置。
解决办法: 每次进行正则校验时,都重新生成正则实例

var regex = new RegExp(/en/g);
regex.test("hhe_en")
// true
regex = new RegExp(/en/g);
/en/g
regex.test("hhe_en")
// true

// 或
/en/g.test("hhe_en")
// true
/en/g.test("hhe_en")
// true

2.回溯地狱导致页面卡死,详细参考
什么是回溯地狱?
正则在匹配的时候回溯过多,造成 CPU 100%,正常服务被阻塞。
使用场景记录:输入框校验,/^(\d+,?)+$/

validator(_, value) {
	const newValue = value ? value.trim() : value;
    if (newValue && !/^(\d+,?)+$/.test(newValue)) {
		return Promise.reject(new Error("输入框内只支持数字和逗号,请检查!"));
	} 
    return Promise.resolve();
}

在这里插入图片描述
js的正则引擎是nfa,所以就有回溯,可能导致匹配过慢。

正则表达式匹配目标字符串时,它从左到右逐个测试表达式的组成部分,看是否能找到匹配项。在遇到量词时,需要决定何时尝试匹配更多字符。在遇到分支时,必须从可选项中选择一个尝试匹配。每当正则做类似的决定时,如果有必要,都会记录其他选择,以便匹配不成功时进行回溯,到最后一个决策点,再重新进行匹配。——《高性能JavaScript》

回溯简单举例: /a(acd|bsc|bcd)/匹配“abcd”,
在这里插入图片描述
匹配步骤如下:
第一步:从正则表达式第一个字符a开始,吃进“abcd”的第一个字符,也是a,匹配成功!
第二步:这时正则表达式遇到了分支,后面有三种匹配可能,分别是acd、bsc、bcd。先选择第一个路径acd,吃入“abcd”的第二个字符,是b,匹配不成功。这时就需要进行一次回溯了(backtrack),把吃进来的最后一个字符b还回去,同时放弃第一个路径,选择第二个路径bsc;
第三步:第二个路径bsc中,第一个字符是b,吃进“abcd”中的第二个字符,也是b,匹配成功!
第四步:第二个路径bsc中,下一个字符是s,吃进“abcd”中的第三个字符是c,匹配失败,又要进行回溯了。把刚刚吃进的c和b还回去,回到第二步的状态,并选择第三个路径bcd;
第五步~第七步:依次匹配bcd和“abcd”中的剩余字符,均匹配成功。
第八步:完成匹配。

正则在线测试工具:regex101
在这里插入图片描述只要不是逗号或者空(换行符、制表符等)都会导致回溯次数过多,因为这里多的字符正则表达式既匹配不到它又不能结束匹配,只能回溯,再遇到同样的问题,再回溯,直到页面奔溃。

优化:

  1. 尽可能的去让正则表达式准确化,越准确的正则表达式匹配时,回溯情况就越少,性能就越高。
  2. 可以做正则匹配的分级,尽可能使用一些性能比较高的正则表达式,先进行一些过滤匹配。在命中需要匹配的条件以后,再使用比较复杂的正则表达式进行匹配。从而避免复杂的正则表达式频繁的被调用。
const newValue = value ? value.trim().split(",").filter(i => i): [];
const length = newValue.length;
if (newValue && length > 0 && newValue.some(i => !/^\d+$/.test(i))) {
	return Promise.reject(new Error("输入框内只支持数字和英文逗号,请检查!"));
} else if (value.match(/^.*?,,+.*$/)) {
	return Promise.reject(new Error("输入格式不正确,请检查!"));
} 
return Promise.resolve();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

seaWind0802

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值