Versuchen:
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);
}
}
}
Ausgabe:
> foo
> bar
> c;qual="baz,blurb"
> d;junk="quux,syzygy"
Mit anderen Worten: Teilen Sie das Komma nur, wenn dieses Komma Null oder eine gerade Anzahl von Anführungszeichen vor sich hat .
Oder etwas freundlicher für die Augen:
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);
}
}
}
das ergibt das gleiche wie das erste Beispiel.
BEARBEITEN
Wie von @MikeFHay in den Kommentaren erwähnt:
Ich bevorzuge die Verwendung von Guavas Splitter , da er vernünftigere Standardeinstellungen hat (siehe Diskussion oben über leere Übereinstimmungen, die von gekürzt werden String#split()
, also habe ich Folgendes getan:
Splitter.on(Pattern.compile(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"))
String line = "equals: =,\"quote: \"\"\",\"comma: ,\""
Sie also nur das überflüssige doppelte Anführungszeichen entfernen müssen Zeichen.-1
auf die Split - Methode param:line.split(regex, -1)
. Siehe: docs.oracle.com/javase/6/docs/api/java/lang/…Splitter.on(Pattern.compile(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"))
.findAllIn("(?s)(?:\".*?\"|[^\",]*)*")
in Kombination mit einem Nachbearbeitungsschritt, bei dem das erste (immer leere) Feld nach jedem nicht leeren Feld übersprungen wird.Während ich im Allgemeinen reguläre Ausdrücke mag, glaube ich, dass für diese Art der zustandsabhängigen Tokenisierung ein einfacher Parser (der in diesem Fall viel einfacher ist, als dieses Wort es klingen lässt) wahrscheinlich eine sauberere Lösung ist, insbesondere im Hinblick auf die Wartbarkeit , z.B:
Wenn Sie die Kommas in den Anführungszeichen nicht beibehalten möchten, können Sie diesen Ansatz vereinfachen (keine Behandlung des Startindex, kein Sonderfall für das letzte Zeichen ), indem Sie Ihre Kommas in Anführungszeichen durch etwas anderes ersetzen und dann durch Kommas teilen:
quelle
http://sourceforge.net/projects/javacsv/
https://github.com/pupi1985/JavaCSV-Reloaded (Verzweigung der vorherigen Bibliothek, die es der generierten Ausgabe ermöglicht, Windows-Zeilenabschlüsse zu verwenden,
\r\n
wenn Windows nicht ausgeführt wird)http://opencsv.sourceforge.net/
CSV-API für Java
Können Sie eine Java-Bibliothek zum Lesen (und möglicherweise Schreiben) von CSV-Dateien empfehlen?
Java lib oder App zum Konvertieren von CSV in XML-Datei?
quelle
Ich würde Bart keine reguläre Antwort empfehlen, ich finde die Parsing-Lösung in diesem speziellen Fall besser (wie Fabian vorgeschlagen hat). Ich habe Regex-Lösung und eigene Parsing-Implementierung ausprobiert. Ich habe Folgendes festgestellt:
Meine Lösung und Test unten.
Natürlich können Sie in diesem Snippet den Wechsel zu else-ifs ändern, wenn Sie sich mit seiner Hässlichkeit unwohl fühlen. Beachten Sie dann die fehlende Unterbrechung nach dem Schalter mit Trennzeichen. StringBuilder wurde stattdessen als StringBuffer ausgewählt, um die Geschwindigkeit zu erhöhen, wenn die Thread-Sicherheit keine Rolle spielt.
quelle
-1
der Teilungsmethode in Barts Antwort ein hinzufügen , werden Sie leere Zeichenfolgen (einschließlich leerer Zeichenfolgen nach dem letzten Komma) abfangen:line.split(regex, -1)
Versuchen Sie einen Lookaround wie
(?!\"),(?!\")
. Dies sollte übereinstimmen,
, die nicht von umgeben sind"
.quelle
(?<!"),(?!")
, aber es wird immer noch nicht funktionieren. Wenn die Zeichenfolge angegeben istone,two,"three,four"
, stimmt sie korrekt mit dem Komma übereinone,two
, stimmt jedoch auch mit dem Komma"three,four"
überein und stimmt nicht mit dem Komma übereintwo,"three
.Sie befinden sich in einem nervigen Grenzbereich, in dem Regexps fast nicht funktionieren (wie Bart betont hat, würde das Entkommen aus den Zitaten das Leben schwer machen), und dennoch scheint ein ausgewachsener Parser übertrieben zu sein.
Wenn Sie wahrscheinlich bald eine größere Komplexität benötigen, würde ich nach einer Parser-Bibliothek suchen. Zum Beispiel dieser
quelle
Ich war ungeduldig und entschied mich, nicht auf Antworten zu warten ... als Referenz sieht es nicht so schwer aus, so etwas zu tun (was für meine Anwendung funktioniert, ich muss mich nicht um entkommene Anführungszeichen kümmern, wie das Zeug in Anführungszeichen ist auf einige eingeschränkte Formen beschränkt):
(Übung für den Leser: Erweitern Sie den Umgang mit maskierten Anführungszeichen, indem Sie auch nach Backslashes suchen.)
quelle
Der einfachste Ansatz besteht darin, Trennzeichen, dh Kommas, nicht mit einer komplexen zusätzlichen Logik abzugleichen, die mit dem übereinstimmt, was tatsächlich beabsichtigt ist (die Daten, bei denen es sich möglicherweise um Zeichenfolgen handelt), sondern nur falsche Trennzeichen auszuschließen, sondern zunächst mit den beabsichtigten Daten übereinzustimmen.
Das Muster besteht aus zwei Alternativen, einer Zeichenfolge in Anführungszeichen (
"[^"]*"
oder".*?"
) oder allem bis zum nächsten Komma ([^,]+
). Um leere Zellen zu unterstützen, müssen wir zulassen, dass das nicht zitierte Element leer ist, und gegebenenfalls das nächste Komma verwenden und den\\G
Anker verwenden:Das Muster enthält auch zwei Erfassungsgruppen, um entweder den Inhalt der angegebenen Zeichenfolge oder den einfachen Inhalt abzurufen.
Dann können wir mit Java 9 ein Array als erhalten
Während ältere Java-Versionen eine Schleife wie benötigen
Das Hinzufügen der Elemente zu einem
List
oder einem Array bleibt dem Leser als Verbrauchsteuer überlassen.Für Java 8 können Sie die
results()
Implementierung dieser Antwort verwenden , um dies wie bei der Java 9-Lösung zu tun.Für gemischte Inhalte mit eingebetteten Zeichenfolgen, wie in der Frage, können Sie einfach verwenden
Aber dann werden die Zeichenfolgen in ihrer zitierten Form gehalten.
quelle
Anstatt Lookahead und andere verrückte Regex zu verwenden, ziehen Sie einfach zuerst die Anführungszeichen heraus. Das heißt, ersetzen Sie für jede Anführungszeichengruppierung diese Gruppierung durch
__IDENTIFIER_1
oder einen anderen Indikator und ordnen Sie diese Gruppierung einer Zuordnung von Zeichenfolge, Zeichenfolge zu.Ersetzen Sie nach dem Teilen durch Komma alle zugeordneten Bezeichner durch die ursprünglichen Zeichenfolgenwerte.
quelle
Was ist mit einem Einzeiler mit String.split ()?
quelle
Ich würde so etwas machen:
quelle