4

我是正则表达式领域的新手。
我将在下面发布的所有内容都是我的代码中的简化示例。

我有一个字符串,假设test_1,some_2,foo,bar_4,我想替换为title: test (1) title: some (2) title: foo () title: bar (4)

我现在拥有的是(有效):

var test = "test_1,some_2,foo,bar_4,";
console.log(test.replace(/(.*?)(?:_(\d))?,/g, "title: $1 ($2)\n"));

输出:

title: test (1)
title: some (2)
title: foo ()
title: bar (4)

为了使事情变得正确,我想摆脱最后一项后的昏迷。列表看起来像test_1,some_2,foo,bar_4 (bar_4 之后没有昏迷)

所以新代码:

var test = "test_1,some_2,foo,bar_4";
console.log(test.replace(/(.*?)(?:_(\d))?(?:,|$)/g, "title: $1 ($2) "));

输出错误。最后有一个额外的空匹配:

title: test (1)
title: some (2)
title: foo ()
title: bar (4)
title:  ()

我的问题是:为什么?如何解决?实际的正则表达式是否有任何可能的改进?

演示 jsFiddle

4

3 回答 3

5

您正在获得最后一个误报匹配,因为您的正则表达式匹配空字符串:

"".replace(/(.*?)(?:_(\d))?(?:,|$)/g, "title: '$1' ('$2') ");

title: '' ('') 

因此,在您的情况下,在所有字符都被消耗后,它将匹配一个空字符串。

您可以通过将第一组更改为非可选来进行控制,考虑到它并不是真正的可选组,因为它显示。

/(.*?)(?:_(\d))?(?:,|$)/g
 --^^--

例如,

var str = "test_1,some_2,foo,bar_4";
test.replace(/([a-z]+)(?:_(\d))?(?:,|$)/gi, "title: '$1' ('$2') ");

title: test (1) title: some (2) title: foo () title: bar (4)

那是,

  • ([a-z]+):匹配至少一个字母字符,并且
  • gi: 使字符串不区分大小写。
于 2012-12-15T13:37:58.803 回答
1

作为最简单的解决方案,您可以在匹配正则表达式之前将尾随逗号添加到原始字符串。

于 2012-12-15T12:16:23.767 回答
1

您的问题是您的模式不仅匹配您想要的内容,而且还匹配空字符串:

(.*?)  # matches any string (including an empty one) not containing \n
(?:_(\d))?  # it is an optional group
(?:,|$)  # it matches a comma or the end of the string

因此,当您的正则表达式引擎根据您的模式评估字符串的结尾时,它会看到:

  • 第一组匹配,因为正在处理一个空字符串
  • 第二组匹配,因为它是可选的
  • 第三组匹配,因为正在处理字符串的结尾

所以整个模式匹配,你得到一个额外的匹配。match使用strings的方法在控制台中可以看得很清楚

> s.match(/(.*?)(?:_(\d))?(?:,|$)/g)
  ["test_1,", "some_2,", "foo,", "bar_4", ""]

您至少有两种解决问题的方法:

  • 以与空字符串不匹配但仍符合您的需要的方式更改模式的第一组(这取决于您必须处理的字符串)
  • 保持正则表达式不变,并通过replace删除不需要的部分来处理返回的字符串

第一个选择是优雅的。第二个可以通过额外的代码轻松实现:

> var result = s.replace(/(.*?)(?:_(\d))?(?:,|$)/g, "title: $1 ($2) ");
> result = result.slice(0, result.lastIndexOf("title"));
  "title: test (1) title: some (2) title: foo () title: bar (4) "
于 2012-12-16T11:40:15.057 回答