Warum gibt "split" für eine leere Zeichenfolge ein nicht leeres Array zurück?

111

Das Teilen auf eine leere Zeichenfolge gibt ein Array der Größe 1 zurück:

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

Beachten Sie, dass dies ein leeres Array zurückgibt:

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

Bitte erkläre :)

oluies
quelle
5
Darüber hinaus scheint es nicht mit dem beobachteten Verhalten übereinzustimmen, wenn die Zeichenfolge nur eine Instanz des Trennzeichens enthält. In diesem Fall ist das Ergebnis effektiv ein leeres Array: ",". Split (","). Length == 0
LD.

Antworten:

37

Aus dem gleichen Grund, dass

",test" split ','

und

",test," split ','

gibt ein Array der Größe 2 zurück. Alles vor der ersten Übereinstimmung wird als erstes Element zurückgegeben.

Daniel C. Sobral
quelle
5
Leere Zeichenfolge ist eine Zeichenfolge, nicht nichts. (irgendwo anders als in Excel)
Raphael
5
@ Raphael Oder in einer Oracle-Datenbank
Austin
7
@ Raphael, in jeder anderen Programmiersprache "".split("wtf").lengthgibt 0 zurück. Nur in JS ist es 1 .: /
Andrey Mikhaylov - lolmaus
11
@ DanielC.Sobral Ok, warum wird "," split ","ein Array von 0 zurückgegeben?
Joan
5
Warum ist nicht alles nach dem letzten Spiel auch zurückgekehrt?
Didier A.
72

Wenn Sie eine Orange nullmal teilen, haben Sie genau ein Stück - die Orange.

Sam Stainsby
quelle
8
Aber die Orange ist nicht leer (idk, wenn das Oluies bedeutet), es ist eine Orange. Vielleicht teilen Sie eine Orange, die dort sein sollte, aber nicht, so dass Sie einen einzigen Wert zurückerhalten: einen leeren Raum xD
Nick Rolando
8
Dies ist ein tiefes Gespräch.
31
Diese Metapher ist sinnvoll "orange".split(','), aber offensichtlich nicht relevant für das Teilen leerer Zeichenfolgen. Wenn ich meinen Mangel an Orange nullmal aufteile, habe ich immer noch keine Orange; Stellen wir das als leere Liste von No-Orangen, als Liste von genau einem No-Orange, als Liste von zwölf No-Orangen dar, oder was? Es geht nicht darum, was wir am Ende haben, sondern wie wir es darstellen.
Matchu
1
Wenn Sie jedoch ein nicht vorhandenes Buch nach Seiten aufteilen, erhalten Sie nichts.
SMUsamaShah
49

Die Java- und Scala-Split-Methoden arbeiten in zwei Schritten:

  • Teilen Sie zuerst die Zeichenfolge nach Trennzeichen. Die natürliche Konsequenz ist, dass, wenn die Zeichenfolge kein Trennzeichen enthält, ein Singleton-Array zurückgegeben wird, das nur die Eingabezeichenfolge enthält.
  • Zweitens entfernen Sie alle leeren Zeichenfolgen ganz rechts. Das ist der Grund",,,".split(",") leeres Array zurückgegeben.

Demnach ist das Ergebnis von "".split(",") wegen des zweiten Schritts ein leeres Array sein, oder?

Es sollte. Leider ist dies ein künstlich eingeführter Eckfall. Und das ist schlecht, aber zumindest ist dokumentiert injava.util.regex.Pattern , wenn Sie einen Blick in der Dokumentation nehmen erinnern:

Für n == 0 ist das Ergebnis wie für n <0, außer dass nachfolgende leere Zeichenfolgen nicht zurückgegeben werden. (Beachten Sie, dass der Fall, in dem die Eingabe selbst eine leere Zeichenfolge ist, wie oben beschrieben speziell ist und der Parameter limit dort nicht gilt.)

Lösung 1: Übergeben Sie immer -1 als zweiten Parameter

Deshalb rate ich Ihnen, immer zu bestehen n == -1 als zweiten Parameter zu übergeben (dies überspringt Schritt 2 oben), es sei denn, Sie wissen genau, was Sie erreichen möchten / Sie sind sicher, dass die leere Zeichenfolge nicht etwas ist, das Ihr Programm als Eingabe erhalten würde.

Lösung 2: Verwenden Sie die Guava Splitter-Klasse

Wenn Sie Guava bereits in Ihrem Projekt verwenden, können Sie die Splitter- Klasse (Dokumentation) ausprobieren . Es hat eine sehr umfangreiche API und macht Ihren Code sehr einfach zu verstehen.

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"
Rok Kralj
quelle
1
+1, dies ist die einzige Antwort, die die Dokumentation tatsächlich zitiert und darauf hinweist, dass sie inkonsistent ist. Ich habe den hervorgehobenen Teil des Kommentars jedoch nicht in meinem JavaDoc gefunden.
Yogu
Ich habe es in java.util.regex.Pattern gefunden, aber es scheint größtenteils weg zu sein. Zum Zeitpunkt des Schreibens war es definitiv als Javadoc im offiziellen OpenJDK-Quellbaum vorhanden. android.googlesource.com/platform/libcore/+/… Vielleicht sollten wir einen Fehler melden?
Rok Kralj
Es wäre eine gute Idee, einen Fehler zu melden - das Verhalten wird definitiv nicht geändert, sollte aber zumindest dokumentiert werden.
Yogu
@RokKralj Android hat die OpenJDK-Bibliothek nicht verwendet, sondern basiert auf Apache Harmony. Vielleicht suchen Sie also am falschen Ort?
lxgr
1
"".split (",", n)generiert mit Oracle JDK 8 ein Ein-Element-Array für n in (-1, 0, 1). Es wäre schön, nur eine Liste nicht leerer Token zu erhalten - möglicherweise ist eine vollständige Regex erforderlich (so etwas wie "[^,\\s]+[^,]*[^,\\s]*").
simon.watts
40

Durch das Teilen einer leeren Zeichenfolge wird die leere Zeichenfolge als erstes Element zurückgegeben. Wenn in der Zielzeichenfolge kein Trennzeichen gefunden wird, erhalten Sie ein Array der Größe 1, das die ursprüngliche Zeichenfolge enthält, auch wenn diese leer ist.

Nick Rolando
quelle
2
Falsch. Split entfernt alle am weitesten rechts liegenden leeren Zeichenfolgen. Daher sollte das Ergebnis ein leeres Array sein. Siehe meine Antwort. ",".split(",")Gibt ein leeres Array zurück.
Rok Kralj
23

"a".split(",")-> "a" deshalb "".split(",")->""

weberjn
quelle
6
Falsch. Split entfernt alle am weitesten rechts liegenden leeren Zeichenfolgen. Daher sollte das Ergebnis ein leeres Array sein. Siehe meine Antwort. ",".split(",")Gibt ein leeres Array zurück.
Rok Kralj
5

In allen Programmiersprachen weiß ich, dass eine leere Zeichenfolge immer noch eine gültige Zeichenfolge ist. Wenn Sie also einen Split mit einem beliebigen Trennzeichen ausführen, wird immer ein einzelnes Elementarray zurückgegeben, wobei dieses Element die leere Zeichenfolge ist. Wenn es ein null (nicht leerer) String wäre, wäre das ein anderes Problem.

brent777
quelle
Ich denke, dies ist eine Bibliotheksfunktion und kein Teil der Sprache. In Google Guava können Sie beispielsweise leere Zeichenfolgen weglassen. > Iterable <String> items = com.google.common.base.Splitter.on (','). OmitEmptyStrings (). Split ("");
Oluies
2

Dieses splitVerhalten wird von Java geerbt, egal ob gut oder schlecht ...
Scala überschreibt die Definition des StringGrundelements nicht.

Beachten Sie, dass Sie das limitArgument verwenden können, um das Verhalten zu ändern :

Der Parameter limit steuert, wie oft das Muster angewendet wird, und wirkt sich daher auf die Länge des resultierenden Arrays aus. Wenn der Grenzwert n größer als Null ist, wird das Muster höchstens n - 1 Mal angewendet, die Länge des Arrays ist nicht größer als n, und der letzte Eintrag des Arrays enthält alle Eingaben über das zuletzt übereinstimmende Trennzeichen hinaus. Wenn n nicht positiv ist, wird das Muster so oft wie möglich angewendet und das Array kann eine beliebige Länge haben. Wenn n Null ist, wird das Muster so oft wie möglich angewendet, das Array kann eine beliebige Länge haben und nachfolgende leere Zeichenfolgen werden verworfen.

dh Sie können festlegen limit=-1, dass das Verhalten (aller?) anderer Sprachen ermittelt wird:

@ ",a,,b,,".split(",")
res1: Array[String] = Array("", "a", "", "b")

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

Es scheint bekannt zu sein, dass das Java-Verhalten ziemlich verwirrend ist, aber:

Das obige Verhalten kann von mindestens Java 5 bis Java 8 beobachtet werden.

Es wurde versucht, das Verhalten so zu ändern, dass beim Teilen einer leeren Zeichenfolge in JDK-6559590 ein leeres Array zurückgegeben wird . Es wurde jedoch bald in JDK-8028321 zurückgesetzt, wenn es an verschiedenen Stellen eine Regression verursacht. Die Änderung schafft es nie in die erste Java 8-Version.

Hinweis: Die Split-Methode war von Anfang an nicht in Java ( nicht in 1.0.2 ), sondern tatsächlich ab mindestens 1.4 (siehe z. B. JSR51 um 2002). Ich untersuche immer noch ...

Was unklar ist, ist, warum Java dies überhaupt gewählt hat (mein Verdacht ist, dass es ursprünglich ein Versehen / Fehler in einem "Randfall" war), aber jetzt unwiderruflich in die Sprache eingebrannt und so bleibt es .

Andy Hayden
quelle
Ich bin mir nicht sicher, ob dies die Frage beantwortet - obwohl dies für das hier angegebene Beispiel zutrifft, hilft es bei der leeren Zeichenfolge nicht -, gibt "".split(",")dennoch ein einzelnes Elementarray wie zurück [""].
DaveyDaveDave
@ DaveyDaveDave ist das erwartete Verhalten jeder anderen Sprache. Das ",,,," ist das bizarre / andere Verhalten in Scala und unterscheidet sich vom "" Fall.
Andy Hayden
0

Leere Zeichenfolgen haben beim Teilen einer Zeichenfolge keinen besonderen Status. Sie können verwenden:

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