如何让JavaScript正则表达式在管道读取文件时匹配所有行
问题原因
核心原因是带g(全局匹配)标志的正则表达式内置的lastIndex属性特性:
- JavaScript中带
g标志的正则实例会自动维护lastIndex属性,记录上一次exec方法匹配结束的下标位置,下一次调用exec时会默认从该位置向后查找匹配内容。 - 逐行匹配独立字符串的场景下,
lastIndex不会自动重置:匹配完第一行后lastIndex被设置为第一行的字符长度,匹配第二行时从该位置向后查找,自然无法匹配到内容;匹配失败后lastIndex会被自动重置为0,因此第三行又能正常匹配,最终表现为固定隔行匹配失败。 - 单独提取行测试仍匹配失败也是同理:如果此前已经用同一个正则实例执行过匹配操作,
lastIndex不为0,第一次匹配该行时就会失败。
解决方法
可从以下三个方案中任选:
- 移除正则中的
g标志(最优方案)
逐行匹配独立字符串的场景不需要全局多匹配能力,移除g标志后,每次调用exec都会默认从字符串开头查找,也不会修改lastIndex属性,完全规避该问题:
// 仅需删除正则末尾的g标志即可 const regex = /(?<email>((?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\]))\s*\|\s*(?<name>([a-zA-Z]{2,}\s[a-zA-Z]{1,}'?-?[a-zA-Z]{2,}\s?([a-zA-Z]{1,})?))\s*\|\s*(?<address>.*)\s*\|\s*(?<country>(\w|\.|\s*){1,})\s*\|\s*(?<phone>(\d|-|\ |\+|\(|\)|\.|\/){7,})/m;
- 每次执行
exec前手动重置lastIndex
如果业务需要保留g标志,每次匹配前手动将lastIndex设置为0即可:
regex.lastIndex = 0; let values = regex.exec(line.trim());
- 改用字符串
match方法执行匹配
替换正则exec方法为字符串的match方法,也可规避该特性带来的问题:
let values = line.trim().match(regex);
内容的提问来源于stack exchange,提问作者Mederic
相关产品推荐
相关产品推荐

