JavaScript正则表达式迭代器提取组。

14
假设我们有以下文本:"1 a,2 b,3 c,4 d"和以下表达式:/\d (\w)/g。
我们想要做的是提取由正则表达式表示的a、b、c、d。
不幸的是,“1 a,2 b,3 c,4 d”.match(/\d (\w)/g)将产生一个数组:1 a,2 b,3 c,4 d,而RegExp.$1将只包含最后一次匹配的组,即RegExp.$1 == 'd'。
我该如何迭代这个正则表达式,以便我也可以提取组…我正在寻找一种内存效率高的解决方案,即某种迭代器对象。
编辑:它需要是通用的。我只在这里提供了一个简单的例子。一种解决方法是循环遍历数组,并为每个项目重新应用没有全局标志的正则表达式,但我认为这种解决方法有点愚蠢,尽管它似乎是唯一的方法。

3
接受您最近的提问。 - hsz
1
需要使用正则表达式吗?你也可以按空格和逗号进行分割,然后提取。 - pimvdb
是的,因为我只提供了一个简单的例子...它比这更复杂。一个解决方案是对数组中的每个匹配重新应用正则表达式,但这很愚蠢。 - Pass
3个回答

15
var myregexp = /\d (\w)/g;
var match = myregexp.exec(subject);
while (match != null) {
    // matched text: match[0]
    // match start: match.index
    // capturing group n: match[n]
    match = myregexp.exec(subject);
}

(毫不掩饰地摘自RegexBuddy


我不太明白这个。match只是一个包含所有元素的数组,比如1 a,2 b,3 c等等...我们无法通过这种方式提取捕获组,除非重新应用正则表达式或者有API可以实现... - Pass
@Pass - 当你使用exex时,匹配将包含整个匹配作为第一个元素,每个组作为其他元素,例如['1 a','a'] - Kobi
match[0] 是整个匹配结果 (例如,"1 a"); match[1] 是第一个捕获组 ("a"). 这段代码遍历了字符串 (见 while 块的最后一行)。 - Tim Pietzcker

2

这个会起作用:

"1 a,2 b,3 c,4 d".match(/\w(?:,|$)/g).join(' '); // => "a, b, c, d"

如果您需要迭代:
var r = /\d (\w)/g,
    s = "1 a,2 b,3 c,4 d",
    m;

while ( m = r.exec(s) ) {
    // `m` is your match, `m[1]` is the letter
}

谢谢您的建议,但这个例子太具体了。我正在寻找一种通用解决方案,因为我的实际代码使用了更复杂的正则表达式。 - Pass

2
一种更简短、更简单(尽管可能不太高效)的解决方案是使用String.prototype.replace。replace 的独特之处在于它隐式地迭代所有匹配项,并为每个匹配项执行一个函数。当然,您可以使用该函数实际替换文本,但除了函数名称之外,这并不是真正所需的:
"1 a,2 b,3 c,4 d".replace(/\d (\w)/g, function(complete_match, matched_letter) {
    console.log(matched_letter);
});

这将会在控制台中记录下 abc,然后是 d。(它也会返回"undefined,undefined,undefined,undefined",但我们这里不关心这个。)
更一般地说,将函数参数替换为 以下参数被调用
function(match, p1, p2, [...], offset, string)
  • match是匹配的子字符串。
  • p1等是匹配的捕获组(如果有)。这些组按照它们对应的左括号的顺序排列(即最左边的先,最外层的先)。如果该组匹配多个子字符串(即在(.)+场景中),则只捕获最后一个(最右边的)子字符串。
  • offset是此匹配在原始字符串中的索引。
  • string是调用replace的字符串。

手动迭代可能更有效率,但这种方法不慢而且更简洁易读;我倾向于使用这种模式而不是手动循环。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接