Pytanie Dlaczego "split" w pustym ciągu zwraca niepustą tablicę?


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

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

Załóżmy, że zwraca pustą tablicę:

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

Proszę wytłumacz :)


76
2018-02-11 00:50


pochodzenie


Ponadto wydaje się niezgodne z zachowaniem zaobserwowanym, gdy ciąg zawiera tylko jedno wystąpienie separatora. W tym przypadku wynikiem jest faktycznie pusta tablica: ",". Split (","). Length == 0 - LD.


Odpowiedzi:


Z tego samego powodu

",test" split ','

i

",test," split ','

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


27
2018-02-11 01:52



Pusty ciąg to ciąg znaków, a nie nic. (gdziekolwiek, ale w Excelu) - Raphael
@Raphael lub w bazie danych Oracle - Austin
@Raphael, w dowolnym innym języku programowania "".split("wtf").length zwraca 0. Tylko w JS jest 1.: / - lolmaus - Andrey Mikhaylov
@ DanielC.Sobral Ok, więc dlaczego "," split "," zwraca tablicę 0? - Joan
Dlaczego nie wszystko również powróciło po ostatnim meczu? - Didier A.


Jeśli podzielisz pomarańczowy zero razy, masz dokładnie jeden kawałek - pomarańczowy.


58
2018-02-11 04:27



... omitEmptyOranges ... - oluies
Ale pomarańcza nie jest pusta (idk, jeśli to jest to, co oznacza oluies), jest pomarańczowa. Może rozdzielając pomarańczę, która powinna tam być, ale nie jest, więc odzyskasz pojedynczą wartość: puste xD przestrzeni - Nick Rolando
To jest głęboka rozmowa.
Ta metafora ma sens "orange".split(','), ale nie ma oczywiście znaczenia dla dzielenia pustych łańcuchów. Jeśli podzielę swój brak pomarańczy zero razy, nadal nie mam pomarańczy; Czy reprezentujemy to jako pustą listę nie-pomarańczy, listę dokładnie jednego nie-pomarańczowego, listę dwunastu nie-pomarańczy, czy co? Nie chodzi o to, z czym mamy do czynienia, ale o to, jak go reprezentujemy. - Matchu
To jest błąd dzielenia przez zero. - Caleb Mauer


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


40
2018-02-11 00:55



Źle. Split usuwa wszystkie prawe puste ciągi, dlatego wynik powinien być pustą tablicą. Zobacz moją odpowiedź. ",".split(",") zwraca pustą tablicę. - Rok Kralj


Metody dzielenia Java i Scala działają w dwóch następujących krokach:

  • Najpierw podziel ciąg znaków za pomocą ogranicznika. Naturalną konsekwencją jest to, że jeśli ciąg nie zawiera separatora, zwracana jest pojedyncza tablica zawierająca tylko ciąg wejściowy,
  • Druga, usuń wszystkie prawe puste ciągi. to jest powód ",,,".split(",") zwraca pustą tablicę.

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

Powinno. Niestety jest to sztucznie wprowadzony narożnik. I to jest złe, ale przynajmniej to jest udokumentowane w java.util.regex.Pattern, jeśli pamiętasz, aby rzucić okiem na dokumentację:

Dla n == 0 wynik jest jak dla n <0, z wyjątkiem ciągów pustych   nie zostaną zwrócone. (Zauważ, że przypadek, w którym dane wejście jest samo w sobie   pusty ciąg znaków jest specjalny, jak opisano powyżej, oraz parametr limitu   nie ma tam zastosowania.)

Rozwiązanie 1: Zawsze jako drugi parametr należy podać -1

Radzę ci więc zawsze przejść n == -1 jako drugi parametr (pominie to krok drugi powyżej), chyba że dokładnie wiesz, co chcesz osiągnąć / masz pewność, że pusty ciąg nie jest czymś, co twój program otrzyma jako dane wejściowe.

TL; DR: Podział pustej struny jest sztucznie wprowadzoną narożną obudową, a dokumentacja ostrzega o tym. Zawsze podawaj -1 jako drugi parametr, aby uniknąć błędów, chyba że masz dobry powód.

Rozwiązanie 2: Użyj klasy Guava Splitter

Jeśli już używasz Guava w swoim projekcie, możesz spróbować Splitter (dokumentacja) klasa. Ma bardzo bogaty interfejs 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"

25
2018-06-13 18:13



+1, jest to jedyna odpowiedź, która faktycznie przytacza dokumentację i wskazuje, że jest niespójna. Jednak nie znalazłem podświetlonej części komentarza w mojej JavaDoc. - Yogu
Znalazłem go w java.util.regex.Pattern, ale wydaje się, że w większości przypadków już go nie ma. W chwili pisania tego dokumentu był on zdecydowanie 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
Dobrym pomysłem byłoby zgłoszenie błędu - zachowanie na pewno nie zostanie zmienione, ale przynajmniej powinno być udokumentowane. - Yogu
@RokKralj Android nie korzystał z biblioteki OpenJDK, ale był oparty na Apache Harmony, więc może szukasz w niewłaściwym miejscu? - lxgr
Co oznacza "sztucznie wprowadzony narożnik"? - Andy Hayden


"a".split(",") -> "a" w związku z tym "".split(",") -> ""


23
2018-04-15 11:06



Źle. Split usuwa wszystkie prawe puste ciągi, dlatego wynik powinien być pustą tablicą. Zobacz moją odpowiedź. ",".split(",") zwraca pustą tablicę. - Rok Kralj


We wszystkich językach programowania wiem, że pusty ciąg jest nadal prawidłowym ciągiem. Zatem robienie podziału za pomocą dowolnego separatora zawsze zwróci pojedynczą tablicę elementów, gdzie ten element jest pusty. Jeśli był to łańcuch pusty (nie pusty), to byłby to inny problem.


4
2018-02-11 00:57



Myślę, że jest to funkcja biblioteczna, a nie część języka. Na przykład w google guava możesz pominąć puste ciągi. > Iterable <String> pieces = com.google.common.base.Splitter.on (','). OmitEmptyStrings (). Split (""); - oluies
.Nie w Ruby :) - Ashitaka


To split zachowanie jest dziedziczone z Java, na lepsze lub na gorsze ...
Scala nie zastępuje definicji z String prymitywny.

Zauważ, że możesz Użyj limit argument, aby zmodyfikować zachowanie:

Parametr limit kontroluje liczbę razy, gdy wzór jest stosowany, a zatem wpływa na długość wynikowej tablicy. Jeśli granica n jest większa od zera, to wzór zostanie zastosowany najwyżej n - 1 razy, długość tablicy będzie nie większa niż n, a ostatni wpis tablicy będzie zawierać wszystkie dane wejściowe poza ostatnim dopasowanym separatorem. Jeśli n nie jest dodatnie, wzorzec będzie stosowany tyle razy, ile to możliwe, a tablica może mieć dowolną długość. Jeśli n wynosi zero, wzorzec będzie stosowany tyle razy, ile będzie to możliwe, tablica może mieć dowolną długość, a ciągi pustych ciągów będą odrzucane.

tj. możesz ustawić limit=-1 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 znane jest zachowanie Java dość mylące ale:

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

Podjęto próbę zmiany zachowania, aby zwrócić pustą tablicę podczas dzielenia pustego ciągu znaków JDK-6559590. Wkrótce jednak wróciło JDK-8028321 kiedy powoduje regresję w różnych miejscach. Zmiana nigdy nie przechodzi do początkowej wersji Java 8.

Uwaga: Metoda podziału nie była od początku w Javie (to jest nie w wersji 1.0.2), ale faktycznie jest tam od co najmniej 1,4 (np JSR51 około 2002). Nadal badam ...

Niejasne jest to, dlaczego Java wybrała to na pierwszym miejscu (podejrzewam, że pierwotnie było to niedopatrzenie / błąd w "przypadku skrajnym"), ale teraz nieodwołalnie wypalono go w języku, a więc pozostaje.


1
2017-10-20 04:47