Javascript Regex - 查找所有可能的匹配项,即使在已经捕获的匹配项中

Posted

技术标签:

【中文标题】Javascript Regex - 查找所有可能的匹配项,即使在已经捕获的匹配项中【英文标题】:Javascript Regex - Find all possible matches, even in already captured matches 【发布时间】:2013-01-29 13:24:00 【问题描述】:

我正在尝试使用带有 javascript 的正则表达式从字符串中获取 所有可能的匹配。看来我这样做的方法与已经匹配的部分字符串不匹配。

变量:

var string = 'A1B1Y:A1B2Y:A1B3Y:A1B4Z:A1B5Y:A1B6Y:A1B7Y:A1B8Z:A1B9Y:A1B10Y:A1B11Y';

var reg = /A[0-9]+B[0-9]+Y:A[0-9]+B[0-9]+Y/g;

代码:

var match = string.match(reg);

我得到的所有匹配结果:

A1B1Y:A1B2Y
A1B5Y:A1B6Y
A1B9Y:A1B10Y

我想要的匹配结果:

A1B1Y:A1B2Y
A1B2Y:A1B3Y
A1B5Y:A1B6Y
A1B6Y:A1B7Y
A1B9Y:A1B10Y
A1B10Y:A1B11Y

在我的脑海中,我希望 A1B1Y:A1B2YA1B2Y:A1B3Y 匹配,即使字符串中的 A1B2Y 需要成为两个匹配项的一部分。

【问题讨论】:

前瞻会得到你想要的匹配,但不幸的是它不会返回前瞻的部分。我还没有找到一种用 javascript 捕获前瞻的方法。也许有,不知道。您的前瞻正则表达式将是: var reg = /A[0-9]+B[0-9]+Y(?=:A[0-9]+B[0-9]+Y)/g; @Mantriur:看我的回答... 也许string.split(":") 然后循环遍历数组可能会给你一个更好的结果。 @nhahtdh 使用 match() 进行了尝试,但当然前瞻是单独的捕获。呸!美丽的答案。 :-) 【参考方案1】:

无需修改您的正则表达式,您可以使用.exec 并操作正则表达式对象的lastIndex 属性,将其设置为在每次匹配后的下半场开始匹配。

var string = 'A1B1Y:A1B2Y:A1B3Y:A1B4Z:A1B5Y:A1B6Y:A1B7Y:A1B8Z:A1B9Y:A1B10Y:A1B11Y';
var reg = /A[0-9]+B[0-9]+Y:A[0-9]+B[0-9]+Y/g;
var matches = [], found;
while (found = reg.exec(string)) 
    matches.push(found[0]);
    reg.lastIndex -= found[0].split(':')[1].length;


console.log(matches);
//["A1B1Y:A1B2Y", "A1B2Y:A1B3Y", "A1B5Y:A1B6Y", "A1B6Y:A1B7Y", "A1B9Y:A1B10Y", "A1B10Y:A1B11Y"]

Demo


根据 Bergi 的评论,您还可以获取最后一个匹配项的索引并将其递增 1,因此它不会从匹配的后半部分开始匹配,而是从第二个字符开始尝试匹配每场比赛开始:

reg.lastIndex = found.index+1;

Demo

最后的结果是一样的。不过,Bergi 的更新代码少了一点,性能也稍差faster。 =]

【讨论】:

很好,这比前瞻、捕获组等要好得多。顺便说一句,reg.lastIndex = found.index+1; 应该足够了,并且与表达式无关 @VinnieCent 没问题。 =] 勾选向上/向下箭头下方的 V,如果它对您有用,则将其标记为已接受。哦,谢谢Bergi,不知道那个属性。 x] 我不得不做 reg.lastIndex = found.index+found[0].length;所以它会从最后一场比赛之后的位置继续。 自我注意:如果没有为 RegExp 设置全局 ("g") 标志,这将不起作用。 (new RegExp("foo", "g")/foo/g【参考方案2】:

您无法从match 获得直接结果,但可以通过RegExp.exec 生成结果并对正则表达式进行一些修改:

var regex = /A[0-9]+B[0-9]+Y(?=(:A[0-9]+B[0-9]+Y))/g;
var input = 'A1B1Y:A1B2Y:A1B3Y:A1B4Z:A1B5Y:A1B6Y:A1B7Y:A1B8Z:A1B9Y:A1B10Y:A1B11Y'
var arr;
var results = [];

while ((arr = regex.exec(input)) !== null) 
    results.push(arr[0] + arr[1]);

为了不消耗文本,我使用了零宽度正向预测(?=pattern),以便可以重新匹配重叠部分。

其实也可以滥用replace方法来达到同样的效果:

var input = 'A1B1Y:A1B2Y:A1B3Y:A1B4Z:A1B5Y:A1B6Y:A1B7Y:A1B8Z:A1B9Y:A1B10Y:A1B11Y'
var results = [];

input.replace(/A[0-9]+B[0-9]+Y(?=(:A[0-9]+B[0-9]+Y))/g, function ($0, $1) 
    results.push($0 + $1);
    return '';
);

但是,由于是replace,所以它做了额外的无用替换工作。

【讨论】:

【参考方案3】:

不幸的是,它不像单个string.match那么简单。

原因是你想要重叠匹配,/g 标志没有给你。

你可以使用前瞻:

var re = /A\d+B\d+Y(?=:A\d+B\d+Y)/g;

但现在你得到:

string.match(re); // ["A1B1Y", "A1B2Y", "A1B5Y", "A1B6Y", "A1B9Y", "A1B10Y"]

原因是前瞻是零宽度,这意味着它只是说明模式是否出现在您尝试匹配的内容之后;它不包括在匹配中。

您可以使用exec 来尝试获取您想要的东西。如果一个正则表达式有/g 标志,你可以重复运行exec 来获得所有的匹配:

// using re from above to get the overlapping matches

var m;
var matches = [];
var re2 = /A\d+B\d+Y:A\d+B\d+Y/g; // make another regex to get what we need

while ((m = re.exec(string)) !== null) 
  // m is a match object, which has the index of the current match
  matches.push(string.substring(m.index).match(re2)[0]);


matches == [
  "A1B1Y:A1B2Y", 
  "A1B2Y:A1B3Y", 
  "A1B5Y:A1B6Y", 
  "A1B6Y:A1B7Y", 
  "A1B9Y:A1B10Y", 
  "A1B10Y:A1B11Y"
];

Here's a fiddle of this in action。打开控制台查看结果

或者,您可以拆分 : 上的原始字符串,然后循环遍历结果数组,取出当 array[i]array[i+1] 都匹配时匹配的那些。

【讨论】:

以上是关于Javascript Regex - 查找所有可能的匹配项,即使在已经捕获的匹配项中的主要内容,如果未能解决你的问题,请参考以下文章

Javascript REGEX 在字符串和掩码中查找信用卡号

使用 RegEx 查找两个 XML 标记之间的所有内容

在Javascript中查找不包含/ indexOf / Regex的字符串中的子字符串

如何使用 Java Regex 查找字符串中的所有重复字符序列?

Regex根据子值查找所有XML值

Regex / Python3 - re.findall() - 查找操作码之间的所有匹配项