为什么具有全局标志的正则表达式给出错误的结果?

2022-08-29 23:49:16

当我使用全局标志和不区分大小写的标志时,这个正则表达式有什么问题?查询是用户生成的输入。结果应该是[真,真]。

var query = 'Foo B';
var re = new RegExp(query, 'gi');
var result = [];
result.push(re.test('Foo Bar'));
result.push(re.test('Foo Bar'));
// result will be [true, false]

var reg = /^a$/g;
for(i = 0; i++ < 10;)
   console.log(reg.test("a"));

答案 1

具有该标志的对象会跟踪发生匹配的最后一个索引,因此在后续匹配中,它将从上次使用的索引开始,而不是 0。看一看:RegExpg

var query = 'Foo B';
var re = new RegExp(query, 'gi');
console.log(re.lastIndex);

console.log(re.test('Foo Bar'));
console.log(re.lastIndex);

console.log(re.test('Foo Bar'));
console.log(re.lastIndex);

如果您不想在每次测试后手动重置为 0,只需删除该标志即可。lastIndexg

以下是规范规定的算法(第 15.10.6.2 节):

RegExp.prototype.exec(string)

根据正则表达式执行字符串的正则表达式匹配,并返回包含匹配结果的 Array 对象,如果字符串不匹配,则返回 null 字符串 ToString(字符串)以查找正则表达式模式的匹配项,如下所示:

  1. R 是这个 RexExp 对象。
  2. S 是 ToString(字符串) 的值。
  3. 长度S 的长度。
  4. 设 lastIndex 是 R 上 lastIndex 属性的值。
  5. 让我成为ToInteger(lastIndex)的值。
  6. 如果全局属性为 false,则设 i = 0。
  7. 如果 i < 0 或 i >长度,则将 R 的最后一个索引属性设置为 0 并返回 null。
  8. 调用 [[Match]],为其提供参数 S 和 i。如果 [[Match]] 返回失败,请转到步骤 9;否则,让 r 成为其状态结果并转到步骤 10。
  9. 设 i = i+1。
  10. 转到步骤 7。
  11. 设 e 为 r 的 endIndex 值。
  12. 如果全局属性为 true,请将 R 的最后一个索引属性设置为 e。
  13. 设 n 是 r 的捕获数组的长度。(这与 15.10.2.1 的 NCapturingParens 的值相同。
  14. 返回具有以下属性的新数组:
  • index 属性设置为匹配的子字符串在完整字符串 S 中的位置。
  • 输入属性设置为 S。
  • 长度属性设置为 n + 1。
  • 0 属性设置为匹配的子字符串(即 S 在偏移量 i(包括)和偏移量 e 排除量之间的部分)。
  • 对于每个整数 i,使得 i > 0,i ≤ n,将名为 ToString(i) 的属性设置为 r 的捕获数组的第 i 个元素。

答案 2

您正在使用单个对象并多次执行它。在每次连续执行时,它都会从最后一个匹配索引继续。RegExp

您需要“重置”正则表达式,以便在每次执行之前从头开始:

result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));
// result is now [true, true]

话虽如此,每次创建一个新的RegExp对象可能更具可读性(开销很小,因为RegExp无论如何都是缓存的):

result.push((/Foo B/gi).test(stringA));
result.push((/Foo B/gi).test(stringB));