【问题标题】:How to capture multiple repeated groups?如何捕获多个重复组?
【发布时间】:2016-08-28 11:14:45
【问题描述】:

我需要捕获相同模式的多个组。假设,我有以下字符串:

HELLO,THERE,WORLD

我写了以下模式

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

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

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

更新:我不想使用split。我现在只需要如何捕获与模式匹配的所有组,而不仅仅是最后一个。

【问题讨论】:

  • 为什么不在,上拆分?
  • 为什么不使用[A-Z]+[^,]+ 来捕获结果
  • rock321987,我已经更新了输入字符串。我需要准确提取遵循上述模式的字符串。而且我需要让所有组都与模式匹配,而不仅仅是最后一个。我想知道如何使用正则表达式。
  • rock321987,什么不清楚?我需要字符串的每个单词都是匹配组,但我的模式只捕获最后一个(“WORLD”)。
  • 使用 answer 查找所有匹配项

标签: regex


【解决方案1】:

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

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

输出

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

【讨论】:

    【解决方案2】:
    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);
    

    【讨论】:

      【解决方案3】:

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

      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]
      

      如您所见,这适用于任意数量的匹配项。

      【讨论】:

        【解决方案4】:

        主要区别是重复捕获的组而不是捕获重复的组

        正如您已经发现的那样,不同之处在于重复捕获的组仅捕获最后一次迭代。捕获重复的组会捕获所有迭代。

        在 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 的示例

        【讨论】:

        • "捕获重复的组会捕获所有迭代。"在您的 regex101 中尝试用 (\w+),? 替换您的正则表达式,它会给您相同的结果。这里的关键是 g 标志,它重复你的模式以匹配多个组。
        • 这是大错特错。 “捕获重复的组捕获所有迭代”:是的,但它只会在一个匹配中捕获所有迭代(包含所有迭代)。您的示例应该是 ((?:\w,?)+) 。正如@thomas-laurent 所说,这里有多个匹配项只是因为 g 标志。无法从一个捕获组中获得多个匹配项。您必须提取和 preg_match_all (或等效函数)重复组。
        【解决方案5】:

        抱歉,不是 Swift,只是手头最接近的语言的概念证明。

        // 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))
        

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

        【讨论】:

          【解决方案6】:

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

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

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

          【讨论】:

          • 这不是问题的解决方案。问题不在于匹配字符串,而在于捕获所有组。此正则表达式仍然只捕获第一个重复组(带逗号)的最后一个匹配项,以及最后一个组中的匹配项(不带逗号)。
          【解决方案7】:

          看完Byte Commander's answer,我想介绍一个可能的微小改进:

          只要您的n 是预先确定的,您就可以生成一个匹配n 单词的正则表达式。例如,如果我想匹配 1 到 3 个单词,则正则表达式:

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

          将匹配下一个句子,带有一个、两个或三个捕获组。

          HELLO,LITTLE,WORLD
          HELLO,WORLD
          HELLO
          

          你可以看到关于这个正则表达式on Regex101的完整详细解释。

          正如我所说,为您希望使用自己喜欢的语言的任何组生成此正则表达式非常容易。由于我不是一个快速的人,这里有一个 ruby​​ 示例:

          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]+/)

          【讨论】:

            【解决方案8】:

            我认为你需要这样的东西......

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

            Python3 中的返回

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

            【讨论】:

            • re.findall('\w+',b) 短 2 个字符。不需要字符类,因为你只有一个表达式
            【解决方案9】:

            只是为了在答案中提供第 2 段的其他示例。我不确定在一场比赛中获得三组而不是使用一组进行三场比赛对您来说有多重要。例如,在 groovy 中:

            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
            

            【讨论】:

              【解决方案10】:

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

              您必须使用您的语言的正则表达式实现函数来查找模式的所有匹配项,然后您必须删除非捕获组的锚点和量词(并且您可以省略非捕获组本身也是如此)。

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

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

              【讨论】:

              • 如何调整它以适应不同数量的字符串?例如你好,世界和你好,那里,我的,世界。我正在寻找一个表达式来处理这两个示例,并且为更长的字符串数组内置了灵活性
              • @Chris 不能一概而论。正如答案所述,一个捕获组只能捕获一件事,并且无法创建动态数量的捕获组。
              猜你喜欢
              • 2015-04-03
              • 2021-09-09
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-05-28
              • 2017-05-25
              • 1970-01-01
              相关资源
              最近更新 更多