在Java中每隔3个逗号拆分一个字符串

8

我有一个字符串,长这样:

0,0,1,2,4,5,3,4,6

我希望返回的是一个被分割后每三个逗号一组的String[]数组,结果应该如下所示:
[ "0,0,1", "2,4,5", "3,4,6" ]

我找到了类似的函数,但它们不会在第n个逗号处分割。


4
你有尝试过自己编写一个函数来解析/拆分它吗? - Jashaszun
一个可能有用的方法是先将0,0,1,2,4,5,3,4,6改成0,0,1|2,4,5|3,4,6,这是一种相当简单的正则表达式替换转换。或者,直接使用Matcher并逐步遍历它(如此处所示)。 - user2246674
我能想到两种方法:一种是在while循环中使用indexOf,另一种是先按逗号分割,然后再将结果按三个一组重新拼接起来。 - flup
4个回答

23

注意:虽然使用split方法的解决方案可能有效(在Java 17上的最后一个测试中),但是它基于一个错误,因为Java中的前瞻应该具有明显的最大长度。理论上,这种限制应该防止我们使用+,但是某种方式上,以\G开头让我们在这里使用+在未来,这个错误可能会被修复,这意味着split将停止工作。

更安全的方法是使用Matcher#find,如下所示:

String data = "0,0,1,2,4,5,3,4,6";
Pattern p = Pattern.compile("\\d+,\\d+,\\d+");//no look-ahead needed
Matcher m = p.matcher(data);
List<String> parts = new ArrayList<>();
while(m.find()){
    parts.add(m.group());
}
String[] result = parts.toArray(new String[0]);

你可以尝试使用 split 方法,配合正则表达式 (?<=\\G\\d+,\\d+,\\d+),

Demo

String data = "0,0,1,2,4,5,3,4,6";
String[] array = data.split("(?<=\\G\\d+,\\d+,\\d+),"); //Magic :) 
// to reveal magic see explanation below answer
for(String s : array){
    System.out.println(s);
}

输出:

0,0,1
2,4,5
3,4,6

解释

  • \\d 表示一个数字,等同于 [0-9],如 0 或者 3
  • \\d+ 表示一个或多个数字,如 1 或者 23
  • \\d+, 表示一个或多个数字后跟逗号,如 1, 或者 234,
  • \\d+,\\d+,\\d+ 表示接受三个数字,中间由逗号分隔,如 12,3,456
  • \\G 表示上一个匹配,或者(在第一次匹配时)字符串的开头
  • (?<=...),正向先行断言,它将匹配前面有描述在(?<=...)的字符和逗号,
  • (?<=\\G\\d+,\\d+,\\d+), 尝试找到有三个数字的逗号,在这个逗号之前有字符串的开始(如你的例子中的^0,0,1)或者之前匹配的逗号,如 2,4,53,4,6

此外,如果你想使用非数字的字符,你也可以使用其他一组字符,如下所示:

  • \\w 匹配字母、数字和下划线_
  • \\S 匹配所有不是空格的字符
  • [^,] 匹配所有不是逗号的字符
  • ...等等。更多信息请参阅Pattern documentation

顺便提一下,这个表单将在第三个、第五个、第七个(以及其他奇数)逗号处分割,例如:split("(?<=\\G\\w+,\\w+,\\w+,\\w+,\\w+),") 将在每个第五个逗号处分割。

要在第二个、第四个、第六个、第八个(以及其他偶数)逗号处分割,你需要用 {1,maxLengthOfNumber} 替换 +,例如:split("(?<=\\G\\w{1,3},\\w{1,3},\\w{1,3},\\w{1,3}),"),将在每个第四个逗号处分割,当数字最多有三位数(0、00、12、000、123、412、999)时。

要在每个第二个逗号处分割,你也可以使用这个正则表达式 split("(?<!\\G\\d+),"),基于我的先前的回答


3
您可以将 \d+ 替换为 [^,]*,这样它就可以与除逗号以外的任何内容配合使用。因此,它将适用于 "a,b,c,f,g,h,x,y,z" 这样的内容。 - agbinfo
@agbinfo 是的,没错,但由于OP询问的是数字,所以我使用了\\d。无论如何,非常感谢您提供的额外信息,我会将其包含在答案中。 - Pshemo
@Pshemo,此外,您可能没有意识到的是,许多可靠的来源都说您不能在Java中执行这种无限回溯的操作...只能使用某些有限形式的可变回溯...所以作为一个正则表达式爱好者,这个答案绝对值得点赞。例如,Jan Goyvaerts指出,Java通过允许有限重复来更进一步。您仍然不能使用星号或加号。实际上,即使是点星或点加号,看起来也没问题。也许这是一个新的Java版本故事(已经在Java 7中出现)。 - zx81
如果我想在第20个逗号的间隔上拆分值,或者说如果该值是动态的,我们不能使用一些变量来放置那个n个数字吗? - b22
@b22 "每隔20个逗号",然后回答应该解释它(如果不清楚,可以指出让你困惑的部分)。"或者说如果该值是动态的",这取决于您对动态值的看法。在使用正则表达式之后,您无法改变其工作方式,但可以在构建它时使用动态值。如果您正在寻找类似于.split("(?<=\\G\\d{1,100}(,\\d{1,100}){"+n+"}),")的内容,那么很遗憾,这将不起作用(很难解释为什么正则表达式无法确定此处的最大长度,因为n将表示现有值)。 - Pshemo
@b22 我猜你最好的选择可能是使用这个答案:https://dev59.com/93TYa4cB1Zd3GeqP0vXo#17892708 - Pshemo

8

必备的 Guava 答案:

String input = "0,0,1,2,4,5,3,4,6";
String delimiter = ",";
int partitionSize = 3;

for (Iterable<String> iterable : Iterables.partition(Splitter.on(delimiter).split(s), partitionSize)) {
    System.out.println(Joiner.on(delimiter).join(iterable));
}

输出:

0,0,1
2,4,5
3,4,6

6
尝试以下操作:

类似下面这样:

public String[] mySplitIntoThree(String str) 
{
    String[] parts = str.split(",");

    List<String> strList = new ArrayList<String>();

    for(int x = 0; x < parts.length - 2; x = x+3) 
    {
        String tmpStr = parts[x] + "," + parts[x+1] + "," + parts[x+2];

        strList.add(tmpStr);
    }

    return strList.toArray(new String[strList.size()]);
}

(您可能需要导入java.util.ArrayList和java.util.List)

3
感谢参加编程道场!这是我的老式 C 风格的答案:
如果我们将逗号之间的位称为“部分”,并将被拆分出来的结果称为“子字符串”,则: n 是目前找到的部分数量, i 是下一个部分的开始, startIndex 是当前子字符串的开始。
遍历部分,每三个部分:截取一个子字符串。
当没有逗号时,在结尾处添加剩余的部分到结果中。
List<String> result = new ArrayList<String>();
int startIndex = 0;
int n = 0;
for (int i = x.indexOf(',') + 1; i > 0; i = x.indexOf(',', i) + 1, n++) {
    if (n % 3 == 2) {
        result.add(x.substring(startIndex, i - 1));
        startIndex = i;
    }
}
result.add(x.substring(startIndex));

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