【问题标题】:Repetative capturing group matches only last occurence重复捕获组仅匹配最后一次出现
【发布时间】:2016-09-21 12:57:25
【问题描述】:

鉴于我正在经历奇怪的捕获组行为,因此遵循文本数据。 当我尝试遍历所有表时,只有最后一行数据。有没有办法维护所有捕获组,而不仅仅是最后一行(每个表的值)?

我正在使用这种模式(?<tabname>\S+)\n\=*\n(?:(\d+)\ *\|\ *(\d+)\n)+

TABLE1
=======
1  | 2
15 | 2
3  | 15

TABLE2
=======
3  | 5
12 | 2
17 | 7

编辑:抱歉我的问题不一致,这里是我的预期和实际输出:

预期的输出将是:

第 1 场,共 2 场:

Group "tabname":    TABLE1
Group 2:    1
Group 3:    2
Group 4:    15
Group 5:    2
Group 6:    3
Group 7:    15

第 2 场,共 2 场:

Group "tabname":    TABLE2
Group 2:    3
Group 3:    5
Group 4:    12
Group 5:    2
Group 6:    17
Group 7:    7

实际输出是:

第 1 场,共 2 场:

Group "tabname":    TABLE1
Group 2:    3
Group 3:    15

第 2 场,共 2 场:

Group "tabname":    TABLE1
Group 2:    17
Group 3:    7

【问题讨论】:

  • 什么是正则表达式风格/语言?你的意思是你有类似(?<tabname>\S+)\n\S*\n(?:(\d+)\s*\|\s*(\d+)(?:$|\n))*的东西吗?请注意,在 Java 中,重复的捕获组总是被重写,并且只保留最后一个。
  • @WiktorStribiżew Java
  • 你想捕捉什么?
  • @rock321987 表名,所有数据为编号的捕获组
  • 所有数据都作为编号的捕获组 - 请您将确切的预期输出添加到问题中,好吗?

标签: java regex backtracking capturing-group


【解决方案1】:

您可以分两次收集数据。第一个正则表达式将只匹配具有所有值的表:

"(?<tabledata>\\S+)\\s+\\S+(?<vals>[|\\d\\s]+)"

demo。接下来,我们将匹配数字并将它们添加到字符串数组(使用简单的\d+ 正则表达式)。

这是一个full Java demo 产生[[TABLE1, 1, 2, 15, 2, 3, 15], [TABLE2, 3, 5, 12, 2, 17, 7]]

import java.util.*;
import java.lang.*;
import java.io.*;
import java.util.regex.*;

class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        String s = "TABLE1\n=======\n1  | 2\n15 | 2\n3  | 15\n\nTABLE2\n=======\n3  | 5\n12 | 2\n17 | 7"; 
        Pattern pattern = Pattern.compile("(?<tabledata>\\S+)\\s+\\S+(?<vals>[|\\d\\s]+)");
        Matcher matcher = pattern.matcher(s);
        List<List<String>> res = new ArrayList<>();
        while (matcher.find()){
            List<String> lst = new ArrayList<>();
            if (matcher.group("tabledata") != null) {
                lst.add(matcher.group("tabledata"));
            }
            if (matcher.group("vals") != null) {
                Matcher m = Pattern.compile("\\d+").matcher(matcher.group("vals"));
                while (m.find()) {
                    lst.add(m.group(0));
                }
            }
            res.add(lst);
        } 
        System.out.println(res); 
    }
}

【讨论】:

  • 哈哈..在我读到(有点)关于\G..我知道你不想让事情复杂化之前,我就是这么想的..+1
  • @rock321987: \G 你的解决方案也是有效的。只需展开缓和的贪婪量词(请参阅how it can be done here
  • 只需(?:(TABLE\d++)|\G)[^T\d]*+(?:T(?!ABLE\d)[^T\d]*+)*(\d+) 就足够了。 Java 还支持所有格量​​词。
  • 关键在于使用否定字符类(在大多数情况下具有否定的前瞻),以便所有后续子模式无法匹配同一位置的同一字符。一些展开的模式可以在智能分组和量词的帮助下使用。
  • 今天好像有很多要读的东西。。回家再看,问有没有问题。。再次感谢
【解决方案2】:

我相信你可以使用这个正则表达式

(?s)(?:(TABLE\d+)|\G)(?:(?!TABLE).)+?(\d+)\s+\|\s+(\d+)

Regex Demo

一点Java的帮助,就可以达到效果

String line = "TABLE1\n=======\n1  | 2\n15 | 2\n3  | 15\n\nTABLE2\n=======\n3  | 5\n12 | 2\n17 | 7";
String pattern = "(?s)(?:(TABLE\\d+)|\\G)(?:(?!TABLE).)+?(\\d+)\\s+\\|\\s+(\\d+)";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(line);
int flag = 0;

while (m.find()) {
    if (m.group(1) != null) {
        flag = 0;
    }

    if (flag == 0) {
        System.out.println(m.group(1) + "\n" + m.group(2) + "\n" + m.group(3));
        flag = 1;
    } else {
        System.out.println(m.group(2) + "\n" + m.group(3)); 
    }
}

Ideone Demo

【讨论】:

  • 仅供参考:经过调和的贪婪令牌非常消耗资源,Java 正则表达式引擎容易出现复杂模式的堆栈溢出问题(即使是短模式,但具有量化的交替组)。是的,代码看起来更简洁,但您仍然需要为结果声明数组/列表。
  • @WiktorStribiżew 感谢您提供的信息..仍在处理我的正则表达式
猜你喜欢
  • 2017-10-07
  • 2020-12-16
  • 1970-01-01
  • 1970-01-01
  • 2016-06-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-15
  • 2021-06-21
相关资源
最近更新 更多