Kotlin和Java中使用正则表达式的String split方法有什么区别?

11
如果我们有一个val txt: kotlin.String = "1;2;3;",想把它分割成数字数组,我们可以尝试以下方法:
val numbers = string.split(";".toRegex())
//gives: [1, 2, 3, ]

CharSequence.split的结果包含尾部空字符串。

另一方面,如果我们看Java String,结果是不同的:

val numbers2 = (string as java.lang.String).split(";")
//gives: [1, 2, 3]

这次使用 java.lang.String.split,结果不包括尾随的空 String。这种行为实际上是有意的,因为相应的 JavaDoc 中写道:

此方法的工作方式就像使用给定表达式和零限制参数调用两个参数的分割方法一样。因此,尾随的空字符串不包含在生成的数组中

然而,在 Kotlin 版本中,0 也是默认的 limit 参数,正如 here 所记录的那样,但在内部 Kotlin 将其映射为负值 -1,当调用 java.util.regex.Pattern::splitcalled:

nativePattern.split(input, if (limit == 0) -1 else limit).asList()

看起来它的工作方式是预期的,但我想知道为什么语言似乎限制了Java API,因为不再提供0的限制。


我不知道他们选择这样做的原因,但至少对我来说,它感觉更直观。如果使用正则表达式,你可以使用负向先行断言:;(?!$);(?!;* $) - Bubletan
1
我一直认为Java的“limit”语义是一种混乱。它是杂乱无章的,自相矛盾的,几乎不可能记住。 - Marko Topolnik
1个回答

15
实现意味着在Kotlin中失去了通过传递limit = 0实现的java.lang.String.split行为。实际上,从我的角度来看,它被删除是为了在Kotlin中实现可能选项之间的一致性。
考虑一个字符串a:b:c:d:和一个模式:
看看我们在Java中可以得到什么: limit < 0[a, b, c, d, ]
limit = 0[a, b, c, d]
limit = 1[a:b:c:d:]
limit = 2[a, b:c:d:]
limit = 3[a, b, c:d:]
limit = 4[a, b, c, d:]
limit = 5[a, b, c, d, ](与limit < 0相同)
limit = 6[a, b, c, d, ]
...
似乎 limit = 0 选项有点独特:它的尾随 : 既不会被另一个条目替换,如 limit < 0limit >= 5,也不会保留在最后一个结果项中(如 1..4 中的 limit)。
我觉得 Kotlin API 在这里提高了一致性: 没有特殊情况失去关于最后分隔符跟随空字符串的信息——它要么作为最后一个结果项中的分隔符,要么作为空的尾随条目。
在我看来,Kotlin 函数似乎更符合 最小惊讶原则。相反,在 java.lang.String.split 中的零限制看起来更像是修改方法语义的特殊值。负值也是如此,显然作为 限制 没有直观意义,并且没有仔细查阅 Javadoc 就不太清楚。

2
这是有道理的,因为在 Kotlin 中很容易操作结果,比如通过 string.split(";".toRegex()).dropLastWhile { it.isEmpty() } 去除尾部空字符串。 - Naetmul
在 Kotlin 中,split 方法的设计是基于 Python 中的 split 方法,以便实现“自我一致性”。 - voddan
4
我对 "ab".split("") 在 Kotlin 中返回 ["","a","b",""],在 JVM 7 的 Java 中返回 ["","a","b"],在 JVM 8 中返回 ["a","b"] 仍然感到非常惊讶。从现在开始,我可能再也不会向 split 传递空字符串了... - Hay
哦,别这样,Voddan,这与大多数其他编程语言一致,Java的是错误的。 - Luiz Felipe

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