为什么在空字符串上执行"split"操作会返回一个非空数组?

135

使用空字符串进行分割会返回一个大小为1的数组:

scala> "".split(',')
res1: Array[String] = Array("")

考虑这将返回一个空数组:

scala> ",,,,".split(',')
res2: Array[String] = Array()
请解释 :)

6
此外,当字符串只包含一个分隔符时,其行为与观察到的行为似乎不一致。在这种情况下,结果实际上是一个空数组:",".split(",").length == 0。 - LD.
9个回答

82

如果你不切割一个橙子,那么你就只有一个整个的橙子。


13
但是橙子并不是空的(我不确定Oluies的意思是否是这样),它是一颗橙子。也许是把原本应该有的一个橙子分开了,所以你得到的是一个单一的价值:一个空的空间 xD。 - Nick Rolando
18
这是一次深刻的对话。 - user195488
47
这个隐喻对于"orange".split(',')是有意义的,但对于分割空字符串并不明显相关。如果我将我的“无橙色”零次分割,我仍然没有橙色;我们将其表示为空的无橙子列表,一个无橙子的列表,十二个无橙子的列表,还是其他什么?这不是关于我们最终得到了什么,而是如何去表示它。 - Matchu
2
但是,如果你把一本不存在的书按页拆分,你将得到什么都没有。 - SMUsamaShah
1
嗯... 0/0 是什么? - Ardent Coder

65

Java和Scala的split方法分为两个步骤:

  • 首先,按照分隔符拆分字符串。自然的结果是,如果字符串不包含分隔符,则返回一个只包含输入字符串的单元素数组。
  • 其次,“删除所有右侧空字符串”。这就是为什么",,,".split(",") 返回空数组的原因。

根据这个规则,"".split(",")的结果应该是一个空数组,因为第二步的缘故,对吗?

是的。不幸的是,这是一种人为引入的特殊情况。而这很糟糕,但至少它在java.util.regex.Pattern的文档中有所描述,如果你记得查看文档:

对于n == 0,结果与n < 0相同,除了不返回尾部空字符串。(请注意,输入本身为空字符串的情况是特殊的,如上所述,并且限制参数在此情况下不适用。)

解决方案1:始终将-1作为第二个参数传递

因此,我建议您始终将n == -1作为第二个参数传递(这将跳过上述第二步),除非您确切地知道自己想要实现什么/您确定空字符串不是程序可能输入的内容。

解决方案2:使用Guava Splitter类

如果您的项目已经在使用Guava,则可以尝试Splitter(文档)类。它有一个非常丰富的API,并使您的代码非常易于理解。

Splitter.on(".").split(".a.b.c.") // "", "a", "b", "c", ""
Splitter.on(",").omitEmptyStrings().split("a,,b,,c") // "a", "b", "c"
Splitter.on(CharMatcher.anyOf(",.")).split("a,b.c") // "a", "b", "c"
Splitter.onPattern("=>?").split("a=b=>c") // "a", "b", "c"
Splitter.on(",").limit(2).split("a,b,c") // "a", "b,c"

2
+1,这是唯一一个引用文档并指出其不一致的答案。然而,我在我的JavaDoc中没有找到评论中突出显示的部分。 - Yogu
1
我在java.util.regex.Pattern中找到了它,但似乎大部分都已经消失了。在撰写本文时,它肯定存在于官方OpenJDK源代码树中作为javadoc。https://android.googlesource.com/platform/libcore/+/ea021b608acfd0789d8751630575270dc4689630/regex/src/main/java/java/util/regex/Pattern.java#381也许我们应该报告一个bug? - Rok Kralj
报告一个错误可能是个好主意——行为肯定不会改变,但至少应该记录下来。 - Yogu
@RokKralj Android没有使用OpenJDK库,而是基于Apache Harmony,所以也许你正在错误的地方寻找? - lxgr
“人为引入的边界条件”是什么意思? - Andy Hayden
1
在Oracle JDK 8中,"".split(",", n)会生成一个元素数组,其中n为(-1,0,1)。最好只获取非空令牌的列表--猜测可能需要完整的正则表达式(类似于"[^,\\s]+[^,]*[^,\\s]*")。 - simon.watts

39

将空字符串分割后,第一个元素为空字符串。如果目标字符串中没有找到分隔符,则会得到一个大小为1的数组,其中包含原始字符串,即使它为空。


2
错误。Split函数会删除所有最右边的空字符串,因此结果应该是一个空数组。请参考我的答案。",".split(",") 返回一个空数组。 - Rok Kralj

36

出于相同的原因

",test" split ','

还有

",test," split ','

将返回一个大小为2的数组。第一个匹配项之前的所有内容作为第一个元素返回。


5
@Raphael 或在 Oracle 数据库中 - Austin
8
@Raphael,在其他编程语言中,"".split("wtf").length 返回 0。只有在 JavaScript 中返回 1。 :/ - Andrey Mikhaylov - lolmaus
12
为什么 "," split "," 返回一个长度为0的数组? - Joan
5
为什么最后一个匹配后面的所有内容都没有返回呢? - Didier A.
答案是它来自Java...所以Java是一个例子,也许是唯一的例子...太神奇了!我想知道为什么Java选择了这个。 - Andy Hayden
显示剩余8条评论

24

"a".split(",") -> "a" 所以 "".split(",") -> ""


8
错误。Split 函数移除所有右侧的空字符串,因此结果应该是一个空数组。请查看我的答案。",".split(",") 返回空数组。 - Rok Kralj

5

在我所知的所有编程语言中,空字符串仍然是一个有效的字符串。因此,使用任何分隔符进行拆分将始终返回一个单元素数组,该元素为空字符串。如果它是一个null(不是空白)字符串,那么这将是一个不同的问题。


我认为这是一个库函数而不是语言的一部分。例如,在Google Guava中,您可以省略空字符串。
Iterable<String> pieces = com.google.common.base.Splitter.on(',').omitEmptyStrings().split("");
- oluies

2
这种split行为是从Java继承而来的,好或坏...
Scala没有覆盖String原语的定义。
请注意,您可以使用limit参数修改行为

limit参数控制模式应用的次数,因此影响生成的数组长度。如果限制n大于零,则模式最多将应用n-1次,数组的长度不会超过n,并且数组的最后一个条目将包含所有超出最后匹配分隔符的输入。如果n为非正,则模式将尽可能多地应用,数组可以具有任何长度。如果n为零,则模式将尽可能多地应用,数组可以具有任何长度,并且将舍弃尾随空字符串。

例如,您可以设置limit=-1以获得(所有?)其他语言的行为:
@ ",a,,b,,".split(",")
res1: Array[String] = Array("", "a", "", "b")

@ ",a,,b,,".split(",", -1)  // limit=-1
res2: Array[String] = Array("", "a", "", "b", "", "")

似乎众所周知,Java的行为相当令人困惑,但是:

上述行为至少可以从Java 5到Java 8中观察到。

曾经试图在JDK-6559590中更改行为,使得在分割空字符串时返回空数组。然而,由于它在各个地方引起了回归,在JDK-8028321中很快就被撤销了。该更改从未进入最初的Java 8版本。

注意:split方法并不是从一开始就存在于Java中(不在1.0.2中),但实际上至少从1.4开始就有了(例如,请参见JSR51,大约是2002年)。我仍在调查中...

不清楚的是为什么 Java 最初选择了这个(我怀疑最初是一个“边缘情况”的疏忽/错误),但现在已经不可逆地融入到语言中,因此它 依然存在

我不确定这个回答是否正确 - 尽管它对于这里给出的例子可能是正确的,但它并不能解决空字符串的情况 - "".split(",") 仍然返回一个只有一个元素的数组,如 [""] - DaveyDaveDave
@DaveyDaveDave 这是其他语言的预期行为。在 Scala 中,",,,," 是奇怪/不同的行为,与 "" 情况不同。 - Andy Hayden

0

空字符串在分割字符串时没有特殊的状态。您可以使用:

Some(str)
  .filter(_ != "")
  .map(_.split(","))
  .getOrElse(Array())

0

使用这个函数,

public static ArrayList<String> split(String body) {
    return new ArrayList<>(Arrays.asList(Optional.ofNullable(body).filter(a->!a.isEmpty()).orElse(",").split(",")));
}

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