在Java中如何从右侧开始按限制拆分字符串?

6

没有限制的拆分会将整个字符串拆分,但如果你设置了一个限制,它会从左边拆分到这个限制。如何通过右边做到同样的效果?

"a.b.c".split("[.]", 2); // returns ["a", "b.c"]

I would want

"a.b.c".splitRight("[.]", 2); // to return ["a.b", "c"]

编辑:我希望有一个通用解决方案,与splited的工作方式类似,但是是反向的。因此,我添加了一个更复杂的示例。

我想要:

"a(->)b(->)c(->)d".splitRight("\\(->\\)", 3); // to return ["a(->)b", "c", "d"]

@blank :D 这个解决方案很有趣,但是计算工作太多了,如果分隔符不是单个字符,我还需要反转它... - aalku
太多的计算?我们有计算机来处理这种事情!但是,确实变得更加复杂。 - blank
想象一下,你需要每秒执行它100000次。 - aalku
4个回答

6
您可以使用正则表达式中的前瞻匹配:
"a.b.c".split("[.](?=[^.]*$)")

您在这里说“我希望只按照那个点进行分割,该点后面没有其他点”。

如果您想按最后N个点进行分割,则可以通过以下方式推广此解决方案(甚至更加丑陋):

"dfsga.sdgdsb.dsgc.dsgsdfg.dsdg.sdfg.sdf".split("[.](?=([^.]*[.]){0,3}[^.]*$)");

3 替换为 N-2

然而,我会编写一个简短的静态方法:

public static String[] splitAtLastDot(String s) {
    int pos = s.lastIndexOf('.');
    if(pos == -1)
        return new String[] {s};
    return new String[] {s.substring(0, pos), s.substring(pos+1)};
}

如果限制为2,则这是一个不错的解决方案。有没有任何变体可以将其作为参数?我不介意在正则表达式字符串中包含类似{2}的内容。而且,该正则表达式是特定于该分隔符的。它不太容易适应任何分隔符。谢谢。 - aalku
我刚刚做了 splitAtLastDot,但我想知道是否有一般的解决方案,而不仅仅是针对限制为2的情况。 - aalku

3
我会尝试使用类似以下的内容: ```

我会尝试通过以下方式:

```
public List<String> splitRight(String string, String regex, int limit) {
    List<String> result = new ArrayList<String>();
    String[] temp = new String[0];
    for(int i = 1; i < limit; i++) {
        if(string.matches(".*"+regex+".*")) {
            temp = string.split(modifyRegex(regex));
            result.add(temp[1]);
            string = temp[0];
        }
    }
    if(temp.length>0) { 
        result.add(temp[0]);
    }
    Collections.reverse(result);
    return result;
}

public String modifyRegex(String regex){
    return regex + "(?!.*" + regex + ".*$)";
}

split的正则表达式被另一个表达式包裹,因此对于\\.,你将得到:\\.(?!.*\\..*$),以匹配并拆分最后一个分隔符。使用此正则表达式多次拆分字符串,将结果数组的第二个元素添加到List中,然后在结果数组的第一个元素上进行下一次拆分。

上述方法对于您的示例字符串的效果如预期。


2
尽管您说反转需要很长时间,这里有一个小程序可以将字符串反转并根据限制进行拆分;
static String[] leftSplit(String input, String regex, int limit) {
    String reveresedInput = new StringBuilder(input).reverse().toString();
    String[] output = reveresedInput.split(regex, limit);
    String tempOutput[] = new String[output.length];
    for(int i = 0;i<output.length;++i) {
        tempOutput[tempOutput.length-i-1] = new StringBuilder(output[i]).reverse().toString();
    }
    return tempOutput;
}

public static void main(String[] args) {
    System.out.println(Arrays.toString(leftSplit("a.b.c", "[.]", 2)));
    System.out.println(Arrays.toString(leftSplit("I want to. Split this. by the. right side", "[.]", 2)));
}

1
如果正则表达式是有方向性的,比如“-->”,那么这种方法就行不通了,而且你也不能总是反转一个正则表达式。 - aalku
1
是的,这个方法在更复杂的正则表达式上可能行不通,但对于更简单的正则表达式应该可以。如果要让它适用于复杂的正则表达式,您还需要反转正则表达式。 - SomeJavaGuy

1

性能不是很好,但可读性比上面的一些示例要好:

List l = Arrays.stream(new StringBuilder("a.b.c").reverse().toString().split("\\.", 2))
.map(StringBuilder::new).map(StringBuilder::reverse).collect(toList());
Collections.reverse(l);

嗨。在最后一个例子中,这种方式是行不通的,因为“->”和“<-”是两个不同的东西。你需要反转分割正则表达式,而这并不总是容易的。 - aalku
@aalku,我们也可以反转正则表达式 "\\)>-\\(",虽然看起来很丑但是能用。 - barbarous

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