Perché "split" su una stringa vuota restituisce un array non vuoto?


111

La divisione su una stringa vuota restituisce un array di dimensione 1:

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

Considera che questo restituisce un array vuoto:

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

Spiega per favore :)


5
Inoltre, sembra incoerente con il comportamento osservato quando la stringa contiene solo un'istanza del separatore. In questo caso il risultato è effettivamente un array vuoto: ",". Split (","). Length == 0
LD.

Risposte:


37

Per lo stesso motivo

",test" split ','

e

",test," split ','

restituirà un array di dimensione 2. Tutto ciò che precede la prima corrispondenza viene restituito come primo elemento.


5
La stringa vuota è una stringa, non niente. (ovunque tranne che in Excel)
Raphael

5
@Raphael O in un database Oracle
Austin

7
@Raphael, in qualsiasi altro linguaggio di programmazione "".split("wtf").lengthrestituisce 0. Solo in JS è 1.: /
Andrey Mikhaylov - lolmaus

11
@ DanielC.Sobral Ok, allora perché "," split ","restituisce un array di 0?
Joan

5
Perché non è tornato tutto dopo l'ultima partita?
Didier A.

72

Se dividi un'arancia zero volte, hai esattamente un pezzo: l'arancia.


8
Ma l'arancia non è vuota (idk se questo è ciò che significava oluies), è un'arancia. Magari dividendo un'arancia che dovrebbe essere lì, ma non lo è, quindi ottieni un singolo valore: uno spazio vuoto xD
Nick Rolando

8
Questa è una conversazione profonda.

31
Questa metafora ha senso "orange".split(','), ma non è ovviamente rilevante per dividere stringhe vuote. Se divido zero volte la mia mancanza di arancione, non ho ancora l'arancia; lo rappresentiamo come una lista vuota di arance senza arance, una lista di esattamente una senza arance, una lista di dodici senza arance o cosa? Non è una questione di cosa ci ritroviamo, ma di come lo rappresentiamo.
Matchu

1
Ma se dividi un libro inesistente per le sue pagine, non otterrai nulla.
SMUsamaShah

49

I metodi split Java e Scala funzionano in due passaggi come questo:

  • Per prima cosa, dividi la stringa per delimitatore. La conseguenza naturale è che se la stringa non contiene il delimitatore, viene restituito un array singleton contenente solo la stringa di input,
  • In secondo luogo, rimuovere tutte le stringhe vuote più a destra. Questo è il motivo per cui ",,,".split(",")restituisce un array vuoto.

In base a ciò, il risultato di "".split(",")dovrebbe essere un array vuoto a causa del secondo passaggio, giusto?

Dovrebbe. Sfortunatamente, questo è un caso d'angolo introdotto artificialmente. E questo è male, ma almeno è documentato in java.util.regex.Pattern, se vi ricordate di dare un'occhiata alla documentazione:

Per n == 0, il risultato è come per n <0, tranne per il fatto che le stringhe vuote finali non verranno restituite. (Si noti che il caso in cui l'input è esso stesso una stringa vuota è speciale, come descritto sopra, e il parametro limit non si applica qui.)

Soluzione 1: passare sempre -1 come secondo parametro

Quindi, ti consiglio di passare sempre n == -1 come secondo parametro (questo salterà il passaggio due sopra), a meno che tu non sappia specificamente cosa vuoi ottenere / sei sicuro che la stringa vuota non è qualcosa che il tuo programma otterrebbe come input.

Soluzione 2: utilizzare la classe Guava Splitter

Se stai già usando Guava nel tuo progetto, puoi provare la classe Splitter (documentazione) . Ha un'API molto ricca e rende il tuo codice molto facile da capire.

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"

1
+1, questa è l'unica risposta che effettivamente cita la documentazione e fa notare che è incoerente. Tuttavia, non ho trovato la parte evidenziata del commento nel mio JavaDoc.
Yogu

L'ho trovato in java.util.regex.Pattern, ma sembra quasi sparito. Al momento della scrittura, era sicuramente presente nell'albero dei sorgenti di OpenJDK ufficiale come javadoc. android.googlesource.com/platform/libcore/+/… Forse dovremmo segnalare un bug?
Rok Kralj

Sarebbe una buona idea segnalare un bug: il comportamento sicuramente non verrà modificato, ma dovrebbe almeno essere documentato.
Yogu

@RokKralj Android non utilizzava la libreria OpenJDK, ma era invece basata su Apache Harmony, quindi forse stai cercando nel posto sbagliato?
lxgr

1
"".split (",", n)genera un array di un elemento per n in (-1, 0, 1) con Oracle JDK 8. Sarebbe bello ottenere solo un elenco di token non vuoti - suppongo che una regex completa potrebbe essere necessaria (qualcosa di simile "[^,\\s]+[^,]*[^,\\s]*").
simon.watts

40

La divisione di una stringa vuota restituisce la stringa vuota come primo elemento. Se non viene trovato alcun delimitatore nella stringa di destinazione, otterrai un array di dimensione 1 che contiene la stringa originale, anche se è vuoto.


2
Sbagliato. Split rimuove tutte le stringhe vuote più a destra, quindi il risultato dovrebbe essere un array vuoto. Vedi la mia risposta. ",".split(",")restituisce un array vuoto.
Rok Kralj

23

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


6
Sbagliato. Split rimuove tutte le stringhe vuote più a destra, quindi il risultato dovrebbe essere un array vuoto. Vedi la mia risposta. ",".split(",")restituisce un array vuoto.
Rok Kralj

5

In tutti i linguaggi di programmazione so che una stringa vuota è ancora una stringa valida. Quindi eseguire una divisione utilizzando qualsiasi delimitatore restituirà sempre un singolo array di elementi in cui quell'elemento è la stringa vuota. Se fosse una stringa nulla (non vuota), sarebbe un problema diverso.


Penso che questa sia una funzione di libreria e non una parte del linguaggio. Ad esempio in google guava potresti omettere stringhe vuote. > Iterable <String> pieces = com.google.common.base.Splitter.on (','). OmitEmptyStrings (). Split ("");
oluies

2

Questo splitcomportamento è ereditato da Java, nel bene e nel male ...
Scala non sovrascrive la definizione del fileString primitiva.

Nota che puoi usare l' limitargomento per modificare il comportamento :

Il parametro limit controlla il numero di volte in cui il pattern viene applicato e quindi influisce sulla lunghezza della matrice risultante. Se il limite n è maggiore di zero, il modello verrà applicato al massimo n - 1 volte, la lunghezza dell'array non sarà maggiore di n e l'ultima voce dell'array conterrà tutto l'input oltre l'ultimo delimitatore corrispondente. Se n è non positivo, il pattern verrà applicato il maggior numero di volte possibile e l'array può avere qualsiasi lunghezza. Se n è zero, il modello verrà applicato quante più volte possibile, l'array può avere qualsiasi lunghezza e le stringhe vuote finali verranno scartate.

cioè puoi impostare il limit=-1per ottenere il comportamento di (tutte?) le altre lingue:

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

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

Sembra essere noto che il comportamento di Java è abbastanza confuso ma:

Il comportamento sopra può essere osservato almeno da Java 5 a Java 8.

Si è verificato un tentativo di modificare il comportamento per restituire un array vuoto durante la divisione di una stringa vuota in JDK-6559590 . Tuttavia, è stato presto ripristinato in JDK-8028321 quando causa la regressione in vari punti. La modifica non è mai stata introdotta nella versione iniziale di Java 8.

Nota: il metodo split non era in Java dall'inizio ( non è in 1.0.2 ) ma in realtà è presente almeno dalla 1.4 (ad esempio, vedere JSR51 circa 2002). Sto ancora indagando ...

Ciò che non è chiaro è il motivo per cui Java ha scelto questo in primo luogo (il mio sospetto è che originariamente fosse una svista / bug in un "caso limite"), ma ora irrevocabilmente inserito nel linguaggio e così rimane .


Non sono sicuro che questo risponda alla domanda - sebbene possa essere vero per l'esempio fornito qui, non aiuta con il caso della stringa vuota - "".split(",")restituisce comunque un singolo array di elementi come [""].
DaveyDaveDave

@DaveyDaveDave che è il comportamento previsto di ogni altra lingua. Il ",,,," è il comportamento bizzarro / diverso in Scala, e diverso dal caso "".
Andy Hayden

0

Le stringhe vuote non hanno uno stato speciale durante la divisione di una stringa. Puoi usare:

Some(str)
  .filter(_ != "")
  .map(_.split(","))
  .getOrElse(Array())
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.