Dlaczego „split” w pustym ciągu zwraca niepustą tablicę?

111

Podział na pusty ciąg zwraca tablicę o rozmiarze 1:

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

Weź pod uwagę, że zwraca to pustą tablicę:

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

Proszę wytłumacz :)

oluies
źródło
5
Ponadto wydaje się niespójne z zachowaniem obserwowanym, gdy ciąg zawiera tylko jedno wystąpienie separatora. W tym przypadku wynikiem jest faktycznie pusta tablica: ",". Split (","). Length == 0
LD.

Odpowiedzi:

37

Z tego samego powodu

",test" split ','

i

",test," split ','

zwróci tablicę o rozmiarze 2. Wszystko przed pierwszym dopasowaniem zostanie zwrócone jako pierwszy element.

Daniel C. Sobral
źródło
5
Pusty ciąg to ciąg, a nie nic. (gdziekolwiek poza Excelem)
Raphael
5
@Raphael Lub w bazie danych Oracle
Austin
7
@Raphael, w każdym innym języku programowania "".split("wtf").lengthzwraca 0. Tylko w JS to 1.: /
Andrey Mikhaylov - lolmaus 22.02.14
11
@ DanielC.Sobral Ok, dlaczego więc "," split ","zwraca tablicę 0?
Joan
5
Dlaczego nie powróciło też wszystko po ostatnim meczu?
Didier A.
72

Jeśli podzielisz pomarańczę zero razy, masz dokładnie jedną część - pomarańczę.

Sam Stainsby
źródło
8
Ale pomarańcza nie jest pusta (nie wiem, czy to właśnie mieli na myśli oluies), jest pomarańczowa. Może podzielić pomarańczę, która powinna tam być, ale jej nie ma, więc otrzymujesz jedną wartość: puste miejsce xD
Nick Rolando.
8
To jest głęboka rozmowa.
31
Ta metafora ma sens "orange".split(','), ale nie jest oczywiście odpowiednia do dzielenia pustych ciągów. Jeśli podzielę brak pomarańczy zero razy, nadal nie mam pomarańczy; Czy przedstawiamy to jako pustą listę bez pomarańczy, listę dokładnie jednego bez pomarańczy, listę dwunastu bez pomarańczy, czy co? Nie jest to kwestia tego, z czym skończymy, ale jak to przedstawiamy.
Matchu
1
Ale jeśli podzielisz nieistniejącą książkę według stron, nic nie otrzymasz.
SMUsamaShah,
49

Metody podziału Java i Scala działają w dwóch krokach:

  • Najpierw podziel ciąg przez separator. Naturalną konsekwencją jest to, że jeśli ciąg nie zawiera separatora, zwracana jest tablica singleton zawierająca tylko ciąg wejściowy,
  • Po drugie, usuń wszystkie skrajne prawe puste ciągi. To jest powód, dla którego ",,,".split(",")zwraca pustą tablicę.

Zgodnie z tym, wynik "".split(",")powinien być pustą tablicą z powodu drugiego kroku, prawda?

Powinno. Niestety jest to sztucznie wprowadzona obudowa narożna. I że jest źle, ale przynajmniej jest udokumentowana w java.util.regex.Pattern, jeśli pamiętać, aby zapoznać się z dokumentacją:

Dla n == 0 wynik jest taki, jak dla n <0, z wyjątkiem końcowych pustych ciągów, które nie zostaną zwrócone. (Zauważ, że przypadek, w którym dane wejściowe jest samym pustym ciągiem, jest specjalny, jak opisano powyżej, a parametr limit nie ma tam zastosowania).

Rozwiązanie 1: Zawsze przekazuj -1 jako drugi parametr

Dlatego radzę zawsze podawać n == -1jako drugi parametr (spowoduje to pominięcie kroku drugiego powyżej), chyba że dokładnie wiesz, co chcesz osiągnąć / jesteś pewien, że pusty łańcuch nie jest czymś, co Twój program otrzymałby jako dane wejściowe.

Rozwiązanie 2: Użyj klasy Guava Splitter

Jeśli używasz już guawy w swoim projekcie, możesz wypróbować klasę Splitter (dokumentacja) . Ma bardzo bogate API i sprawia, że ​​kod jest bardzo łatwy do zrozumienia.

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
źródło
1
+1, jest to jedyna odpowiedź, która faktycznie cytuje dokumentację i wskazuje, że jest ona niespójna. Jednak nie znalazłem podświetlonej części komentarza w moim JavaDoc.
Yogu
Znalazłem go w java.util.regex.Pattern, ale wydaje się, że w większości zniknął. W chwili pisania tego tekstu na pewno był obecny w oficjalnym drzewie źródłowym OpenJDK jako javadoc. android.googlesource.com/platform/libcore/+/… Może powinniśmy zgłosić błąd?
Rok Kralj
Byłoby dobrym pomysłem zgłoszenie błędu - zachowanie na pewno nie zostanie zmienione, ale powinno być przynajmniej udokumentowane.
Yogu
@RokKralj Android nie korzystał z biblioteki OpenJDK, ale zamiast tego był oparty na Apache Harmony, więc może szukasz w złym miejscu?
lxgr
1
"".split (",", n)generuje jednoelementową tablicę dla n in (-1, 0, 1) z Oracle JDK 8. Byłoby miło otrzymać tylko listę niepustych tokenów - zgadnij, że może być konieczne pełne wyrażenie regularne (coś w rodzaju "[^,\\s]+[^,]*[^,\\s]*").
simon.watts
40

Dzielenie pustego ciągu zwraca pusty ciąg jako pierwszy element. Jeśli w ciągu docelowym nie zostanie znaleziony separator, otrzymasz tablicę o rozmiarze 1, która zawiera oryginalny ciąg, nawet jeśli jest on pusty.

Nick Rolando
źródło
2
Źle. Split usuwa wszystkie skrajne prawe puste ciągi, dlatego wynik powinien być pustą tablicą. Zobacz moją odpowiedź. ",".split(",")zwraca pustą tablicę.
Rok Kralj
23

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

weberjn
źródło
6
Źle. Split usuwa wszystkie skrajne prawe puste ciągi, dlatego wynik powinien być pustą tablicą. Zobacz moją odpowiedź. ",".split(",")zwraca pustą tablicę.
Rok Kralj
5

Wiem, że we wszystkich językach programowania pusty ciąg jest nadal prawidłowym ciągiem. Zatem podział za pomocą dowolnego separatora zawsze zwróci tablicę pojedynczego elementu, w której ten element jest pustym ciągiem. Gdyby był to ciąg pusty (nie pusty), byłby to inny problem.

brent777
źródło
Myślę, że jest to funkcja biblioteczna, a nie część języka. Na przykład w google guava można pominąć puste ciągi znaków. > Iterowalne <String> pieces = com.google.common.base.Splitter.on (','). OmitEmptyStrings (). Split ("");
oluies
2

To splitzachowanie jest dziedziczone po Javie, na dobre lub na złe ...
Scala nie zastępuje definicji z Stringpierwotnego.

Zauważ, że możesz użyć limitargumentu, aby zmodyfikować zachowanie :

Parametr limit określa, ile razy wzór jest stosowany, a zatem wpływa na długość wynikowej tablicy. Jeśli limit n jest większy od zera, to wzorzec zostanie zastosowany najwyżej n - 1 razy, długość tablicy nie będzie większa niż n, a ostatni wpis tablicy będzie zawierał wszystkie dane wejściowe poza ostatnim dopasowanym ogranicznikiem. Jeśli n jest niedodatnie, wzór zostanie zastosowany tyle razy, ile to możliwe, a tablica może mieć dowolną długość. Jeśli n wynosi zero, to wzór zostanie zastosowany tyle razy, ile to możliwe, tablica może mieć dowolną długość, a końcowe puste ciągi zostaną odrzucone.

tzn. możesz ustawić, limit=-1aby uzyskać zachowanie (wszystkich?) innych języków:

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

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

Wydaje się, że dobrze wiadomo, że zachowanie Javy jest dość zagmatwane, ale:

Powyższe zachowanie można zaobserwować od co najmniej Java 5 do Java 8.

Podjęto próbę zmiany zachowania w celu zwrócenia pustej tablicy podczas dzielenia pustego łańcucha w JDK-6559590 . Jednak wkrótce został przywrócony w JDK-8028321, gdy powoduje regresję w różnych miejscach. Zmiana nigdy nie pojawia się w pierwszej wersji Java 8.

Uwaga: metoda podziału nie była w Javie od początku (nie ma jej w wersji 1.0.2 ), ale w rzeczywistości istnieje od co najmniej 1.4 (np. Patrz JSR51 około 2002). Wciąż prowadzę dochodzenie ...

Nie jest jasne, dlaczego Java wybrała to w pierwszej kolejności (podejrzewam, że pierwotnie był to przeoczenie / błąd w „skrajnym przypadku”), ale teraz nieodwołalnie wypalił się w języku i tak pozostaje .

Andy Hayden
źródło
Nie jestem pewien, czy to odpowiada na pytanie - chociaż może to być prawda w podanym tutaj przykładzie, nie pomaga to w przypadku pustego ciągu - "".split(",")nadal zwraca tablicę pojedynczego elementu, taką jak [""].
DaveyDaveDave
@DaveyDaveDave to oczekiwane zachowanie w każdym innym języku. „,,,” to dziwne / odmienne zachowanie w Scali i różni się od przypadku „”.
Andy Hayden,
0

Pusty łańcuch nie ma specjalnego statusu podczas dzielenia łańcucha. Możesz użyć:

Some(str)
  .filter(_ != "")
  .map(_.split(","))
  .getOrElse(Array())
Hanan Oanunu
źródło