如何捕获多个重复的组?

我需要捕获同一模式的多个组:

HELLO,THERE,WORLD

我写了下面的模式

^(?:([A-Z]+),?)+$

我想让它做的是捕捉每一个单词,这样第1组是: “ HELLO”,第2组是“ THERE”,第3组是“ WORLD”。我的正则表达式实际上捕获的只是最后一个,也就是“ WORLD”。

我正在测试我的正则表达式 给你,我想用它与 Swift (也许在 Swift 中有一种方法以某种方式获得中间结果,这样我就可以使用它们?)

更新: 我不想使用 split。我现在只需要知道如何捕获所有符合模式的群体,而不仅仅是最后一个。

232429 次浏览

对于模式中的一个组,在该组中只能得到一个确切的结果。如果您的捕获组被模式重复(您在周围的非捕获组上使用了 +量词) ,则只存储与之匹配的最后一个值。

必须使用语言的 regex 实现函数来实现模式的 找到所有匹配的,然后必须删除非捕获组的锚和量词(也可以省略非捕获组本身)。

或者,展开您的正则表达式,让模式包含您希望在结果中获得的每个组的一个捕获组:

^([A-Z]+),([A-Z]+),([A-Z]+)$

只是为了在答案中提供第2段的附加例子。我不知道对你来说,在一场比赛中获得三组比赛而不是使用一组比赛中获得三组比赛有多重要。例如:

def subject = "HELLO,THERE,WORLD"
def pat = "([A-Z]+)"
def m = (subject =~ pat)
m.eachWithIndex{ g,i ->
println "Match #$i: ${g[1]}"
}


Match #0: HELLO
Match #1: THERE
Match #2: WORLD

我觉得你需要这样的东西。

b="HELLO,THERE,WORLD"
re.findall('[\w]+',b)

在 Python 3中将返回

['HELLO', 'THERE', 'WORLD']

在阅读了 字节指挥官的回答之后,我想介绍一个小小的可能的改进:

只要 n是预先确定的,就可以生成匹配 n单词的 regexp。例如,如果我想匹配1到3个单词,regexp:

^([A-Z]+)(?:,([A-Z]+))?(?:,([A-Z]+))?$

将匹配下一个句子,一个,两个或三个捕捉组。

HELLO,LITTLE,WORLD
HELLO,WORLD
HELLO

您可以看到关于这个正则表达式 在 Regex101上的详细说明。

正如我所说的,使用您喜欢的语言为任何组生成 regexp 都非常容易。因为我不是一个敏捷的家伙,这里有一个红宝石的例子:

def make_regexp(group_regexp, count: 3, delimiter: ",")
regexp_str = "^(#{group_regexp})"
(count - 1).times.each do
regexp_str += "(?:#{delimiter}(#{group_regexp}))?"
end
regexp_str += "$"
return regexp_str
end


puts make_regexp("[A-Z]+")

也就是说,我建议在这种情况下不要使用正则表达式,根据您的需要,还有许多其他很棒的工具,从简单的 split到一些标记化模式。恕我直言,正则表达式不在其中。例如,在 Ruby 中,我会使用类似于 str.split(",")str.scan(/[A-Z]+/)的东西

我知道我的答案来得很晚,但它今天发生在我身上,我用以下方法解决了它:

^(([A-Z]+),)+([A-Z]+)$

因此,第一组 (([A-Z]+),)+将匹配所有的重复模式,除了最后一个 ([A-Z]+)将匹配最后一个。无论字符串中有多少重复的组,这都是动态的。

您实际上有一个将匹配多次的捕获组,而不是多个捕获组。

Javascript (js)解决方案:

let string = "HI,THERE,TOM";
let myRegexp = /([A-Z]+),?/g;       // modify as you like
let match = myRegexp.exec(string);  // js function, output described below
while (match != null) {             // loops through matches
console.log(match[1]);            // do whatever you want with each match
match = myRegexp.exec(string);    // find next match
}

句法:

// matched text: match[0]
// match start: match.index
// capturing group n: match[n]

正如您所看到的,这将适用于任意数量的匹配。

抱歉,不是斯威夫特,只是用最接近的语言证明一下概念。

// JavaScript POC. Output:
// Matches:  ["GOODBYE","CRUEL","WORLD","IM","LEAVING","U","TODAY"]


let str = `GOODBYE,CRUEL,WORLD,IM,LEAVING,U,TODAY`
let matches = [];


function recurse(str, matches) {
let regex = /^((,?([A-Z]+))+)$/gm
let m
while ((m = regex.exec(str)) !== null) {
matches.unshift(m[3])
return str.replace(m[2], '')
}
return "bzzt!"
}


while ((str = recurse(str, matches)) != "bzzt!") ;
console.log("Matches: ", JSON.stringify(matches))

注意: 如果您真的要使用它,您应该使用正则表达式匹配函数给出的匹配位置,而不是字符串替换。

关键的区别是 重复被俘虏的群体而不是 捕捉重复的群体

正如您已经发现的,区别在于重复捕获的组只捕获最后一次迭代。捕获重复的组捕获所有迭代。

在 PCRE (PHP)中:

((?:\w+)+),?
Match 1, Group 1.    0-5      HELLO
Match 2, Group 1.    6-11     THERE
Match 3, Group 1.    12-20    BRUTALLY
Match 4, Group 1.    21-26    CRUEL
Match 5, Group 1.    27-32    WORLD

因为所有捕获都在第1组中,所以只需要 $1进行替换。

我使用了下面这个正则表达式的一般形式:

((?:\{\{RE}})+)

Regex101的例子

  1. 设计一个匹配列表中每个特定元素而不是整个列表的正则表达式。使用/g
  2. 对匹配项进行迭代,从任何垃圾中清除它们,比如混入的列表分隔符。您可能需要另一个正则表达式,或者可以使用简单的替换子字符串方法。

示例代码在 JS 中,抱歉:)这个想法必须足够清楚。

    const string = 'HELLO,THERE,WORLD';


// First use following regex matches each of the list items separately:
const captureListElement = /^[^,]+|,\w+/g;
const matches = string.match(captureListElement);


// Some of the matches may include the separator, so we have to clean them:
const cleanMatches = matches.map(match => match.replace(',',''));


console.log(cleanMatches);

为正则表达式重复组中的 A-Z 模式。

data="HELLO,THERE,WORLD"
pattern=r"([a-zA-Z]+)"
matches=re.findall(pattern,data)
print(matches)

输出

['HELLO', 'THERE', 'WORLD']

正如所讨论的,尝试代码的问题在于有一个捕获组反复匹配,因此最终只能保留最后一个匹配。

相反,指示正则表达式匹配(并捕获)字符串中的所有模式实例,这可以在任何正则表达式实现(语言)中完成。所以,为此设计一个正则表达式模式。

所示示例数据的定义属性是,感兴趣的模式由逗号分隔,因此我们可以使用否定字符类来匹配除逗号之外的任何内容

[^,]+

全局匹配(捕获)——获取字符串中的所有匹配。

如果您的模式需要更多限制,请调整排除列表。例如,捕获由任何列出的标点符号分隔的单词

[^,.!-]+

这将从 hi,there-again!中提取所有单词,而不使用标点符号。(-应该在字符类中给定为第一个或最后一个。)

用巨蟒

import re


string = "HELLO,THERE,WORLD"


pattern = r"([^,]+)"
matches = re.findall(pattern,string)


print(matches)

在 Perl 中(和许多其他兼容系统中)

use warnings;
use strict;
use feature 'say';


my $string = 'HELLO,THERE,WORLD';


my @matches = $string =~ /([^,]+)/g;


say "@matches";

(在这个特定的例子中,捕获 ()实际上是不需要的,因为我们收集了所有匹配的内容。但它们不会造成伤害,而且一般来说,它们是必要的。)