Próbować:
public class Main {
public static void main(String[] args) {
String line = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\"";
String[] tokens = line.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1);
for(String t : tokens) {
System.out.println("> "+t);
}
}
}
Wynik:
> foo
> bar
> c;qual="baz,blurb"
> d;junk="quux,syzygy"
Innymi słowy: podziel przecinek tylko wtedy, gdy przecinek ten ma zero lub parzystą liczbę cudzysłowów przed nim .
Lub nieco bardziej przyjazny dla oczu:
public class Main {
public static void main(String[] args) {
String line = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\"";
String otherThanQuote = " [^\"] ";
String quotedString = String.format(" \" %s* \" ", otherThanQuote);
String regex = String.format("(?x) "+ // enable comments, ignore white spaces
", "+ // match a comma
"(?= "+ // start positive look ahead
" (?: "+ // start non-capturing group 1
" %s* "+ // match 'otherThanQuote' zero or more times
" %s "+ // match 'quotedString'
" )* "+ // end group 1 and repeat it zero or more times
" %s* "+ // match 'otherThanQuote'
" $ "+ // match the end of the string
") ", // stop positive look ahead
otherThanQuote, quotedString, otherThanQuote);
String[] tokens = line.split(regex, -1);
for(String t : tokens) {
System.out.println("> "+t);
}
}
}
co daje taki sam jak pierwszy przykład.
EDYTOWAĆ
Jak wspomniano w @MikeFHay w komentarzach:
Wolę używać rozgałęziacza Guavy , ponieważ ma on domyślne ustawienia (patrz dyskusja powyżej na temat przycinania pustych dopasowań String#split()
, więc zrobiłem:
Splitter.on(Pattern.compile(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"))
String line = "equals: =,\"quote: \"\"\",\"comma: ,\""
wszystko, co musisz zrobić, to usunąć obce cudzysłowy podwójne postacie.-1
do podzielonego metody param:line.split(regex, -1)
. Zobacz: docs.oracle.com/javase/6/docs/api/java/lang/…Splitter.on(Pattern.compile(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"))
.findAllIn("(?s)(?:\".*?\"|[^\",]*)*")
w połączeniu z krokiem przetwarzania końcowego, aby pominąć pierwsze (zawsze puste) pole następujące po każdym niepustym polu.Chociaż ogólnie lubię wyrażenia regularne, dla tego rodzaju tokenizacji zależnej od stanu uważam, że prosty parser (który w tym przypadku jest znacznie prostszy niż to słowo mogłoby zabrzmieć) jest prawdopodobnie czystszym rozwiązaniem, w szczególności w odniesieniu do łatwości konserwacji , np .:
Jeśli nie zależy ci na zachowaniu przecinków w cudzysłowie, możesz uprościć to podejście (bez obsługi indeksu początkowego, żadnego specjalnego przypadku ostatniego znaku ), zastępując przecinki w cudzysłowach czymś innym, a następnie rozdzielając je przecinkami:
źródło
http://sourceforge.net/projects/javacsv/
https://github.com/pupi1985/JavaCSV-Reloaded (rozwidlenie poprzedniej biblioteki, która pozwoli generowanym wyjściom na zakończenie linii Windows,
\r\n
gdy nie jest uruchomiony Windows)http://opencsv.sourceforge.net/
CSV API dla Java
Czy możesz polecić bibliotekę Java do odczytu (i ewentualnie zapisu) plików CSV?
Lib Java lub aplikacja do konwersji CSV do pliku XML?
źródło
Nie zalecałbym odpowiedzi Bartka, w tym konkretnym przypadku znajduję lepsze rozwiązanie do analizowania (jak zaproponował Fabian). Próbowałem rozwiązania regex i własnej implementacji parsowania. Odkryłem, że:
Moje rozwiązanie i test poniżej.
Oczywiście możesz swobodnie zmienić w tym fragmencie przejście na „else-ifs”, jeśli czujesz się niekomfortowo z powodu jego brzydoty. Zwróć uwagę na brak przerwy po przełączniku z separatorem. Zamiast tego StringBuilder został wybrany na StringBuffer, aby zwiększyć prędkość, w której bezpieczeństwo wątków jest nieistotne.
źródło
-1
do metody podziału w odpowiedzi Barta, złapiesz puste ciągi (w tym puste ciągi po ostatnim przecinku):line.split(regex, -1)
Spróbuj spojrzeć jak
(?!\"),(?!\")
. To powinno pasować,
, które nie są otoczone"
.źródło
(?<!"),(?!")
, ale to nadal nie działa. Biorąc pod uwagę ciągone,two,"three,four"
, poprawnie pasuje do przecinkaone,two
, ale również pasuje do przecinka"three,four"
i nie pasuje do jednegotwo,"three
.Znajdujesz się w irytującym obszarze granicznym, w którym wyrażenia regularne prawie nie wystarczą (jak zauważył Bart, unikanie cytatów utrudniłoby życie), a jednak pełny parser wydaje się przesadą.
Jeśli w najbliższym czasie będziesz potrzebować większej złożoności, poszukałbym biblioteki parserów. Na przykład ten
źródło
Byłem niecierpliwy i postanowiłem nie czekać na odpowiedzi ... w celach informacyjnych nie wydaje się to tak trudne, aby zrobić coś takiego (co działa w mojej aplikacji, nie muszę się martwić o ucieczkę cytatów, ponieważ to w cytatach jest ograniczony do kilku form ograniczonych):
(ćwiczenie dla czytelnika: rozciągnij się na obsługę cytowanych cytatów poprzez wyszukiwanie odwrotnych ukośników).
źródło
Najprostszym podejściem nie jest dopasowanie ograniczników, tj. Przecinków, ze złożoną dodatkową logiką, aby dopasować to, co jest rzeczywiście zamierzone (dane, które mogą być ciągami cytowanymi), aby wykluczyć fałszywe ograniczniki, ale raczej dopasować zamierzone dane w pierwszej kolejności.
Wzór składa się z dwóch alternatyw, cytowanego ciągu (
"[^"]*"
lub".*?"
) lub wszystkiego do następnego przecinka ([^,]+
). Aby wesprzeć puste komórki, musimy pozwolić, aby niecytowany element był pusty i zużyć następny przecinek, jeśli taki istnieje, i użyć\\G
kotwicy:Wzorzec zawiera również dwie grupy przechwytywania, aby uzyskać albo treść cytowanego ciągu, albo zwykłą treść.
Następnie, w Javie 9, możemy uzyskać tablicę jako
podczas gdy starsze wersje Java wymagają pętli
Dodanie elementów do
List
tablicy lub tablicy pozostawia czytelnikowi akcyzę.W przypadku języka Java 8 można użyć
results()
implementacji tej odpowiedzi , aby zrobić to jak rozwiązanie Java 9.W przypadku zawartości mieszanej z osadzonymi ciągami, jak w pytaniu, możesz po prostu użyć
Ale potem ciągi są przechowywane w ich cytowanej formie.
źródło
Zamiast używać lookahead i innych zwariowanych wyrażeń regularnych, najpierw wyciągnij cytaty. Oznacza to, że dla każdej grupy cytatów zamień tę grupę na
__IDENTIFIER_1
inny lub inny wskaźnik i zamapuj tę grupę na mapę ciągu znaków.Po podzieleniu przecinkiem zamień wszystkie zamapowane identyfikatory na oryginalne wartości ciągu.
źródło
co z linią jednowierszową za pomocą String.split ()?
źródło
Zrobiłbym coś takiego:
źródło