Parsen einer CSV-Datei für eine eindeutige Zeile mit der neuen Java-8-Streams-API
Ich bin versucht, die neue Java 8 Streams API (für die ich bin ein absoluter Neuling) analysieren für eine bestimmte Zeile (die mit "Neda" in der name-Spalte) in eine CSV-Datei. Anhand der folgenden Artikel für die motivation, die ich geändert und behoben einige Fehler und so konnte ich parse die Datei mit 3 Spalten 'name', 'Alter' und 'Höhe'.
name,age,height
Marianne,12,61
Julie,13,73
Neda,14,66
Julia,15,62
Maryam,18,70
Den Analyse-code ist wie folgt:
@Override
public void init() throws Exception {
Map<String, String> params = getParameters().getNamed();
if (params.containsKey("csvfile")) {
Path path = Paths.get(params.get("csvfile"));
if (Files.exists(path)){
//use the new java 8 streams api to read the CSV column headings
Stream<String> lines = Files.lines(path);
List<String> columns = lines
.findFirst()
.map((line) -> Arrays.asList(line.split(",")))
.get();
columns.forEach((l)->System.out.println(l));
//find the relevant sections from the CSV file
//we are only interested in the row with Neda's name
int nameIndex = columns.indexOf("name");
int ageIndex columns.indexOf("age");
int heightIndex = columns.indexOf("height");
//we need to know the index positions of the
//have to re-read the csv file to extract the values
lines = Files.lines(path);
List<List<String>> values = lines
.skip(1)
.map((line) -> Arrays.asList(line.split(",")))
.collect(Collectors.toList());
values.forEach((l)->System.out.println(l));
}
}
}
Gibt es eine Möglichkeit, um zu vermeiden, re-Lektüre, die Datei nach der Extraktion der Kopfzeile? Das ist zwar eine sehr kleine Beispiel-Datei, ich werde die Anwendung dieser Logik zu einer großen CSV Datei.
Ist es Technik, die die Verwendung der streams-API zu erstellen, die eine Zuordnung zwischen den extrahierten Spaltennamen (in der ersten überprüfung der Datei), um die Werte in die restlichen Zeilen?
Wie kann ich wieder nur eine Zeile in der form List<String>
(statt List<List<String>>
mit allen Zeilen). Ich würde lieber nur die Zeile als eine Zuordnung zwischen den Namen der Spalten und Ihre entsprechenden Werte. (ein bisschen wie ein ResultSet in JDBC). Ich sehe ein Sammler.mapMerger-Funktion, die hilfreich sein könnten hier, aber ich habe keine Ahnung, wie es zu benutzen.
- Warum nicht speichern Sie die Linien auf der ersten Lesen und stream ab?
- Beachten Sie, dass dies nicht funktioniert für einige ansonsten perfekt gültige CSV-Dateien, wie man mit der Linie
"Neda",14,66
Du musst angemeldet sein, um einen Kommentar abzugeben.
Verwenden
BufferedReader
ausdrücklich:Files.lines(…)
auch resorts zuBufferedReader.lines(…)
. Der einzige Unterschied ist, dassFiles.lines
konfigurieren der stream so, dass die Schließung der stream wird in der Nähe der Leser, die brauchen wir nicht hier, als das explizitetry(…)
- Anweisung sorgt bereits für die Schließung derBufferedReader
.Beachten Sie, dass es keine Garantie über den Zustand der Leser nach der stream zurückgegeben
lines()
verarbeitet worden, aber wir können sicher Zeilen gelesen vor Durchführung der stream-operation.Zuerst, Ihre Sorge, dass dieser code liest die Datei zweimal, ist nicht gegründet. Eigentlich
- Dateien.lines
gibt einen Stream der Linien, der faul ist-aufgefüllt. So, der erste Teil der code liest nur die erste Zeile und der zweite Teil des Codes liest den rest (es liest die erste Zeile ein zweites mal, obwohl, selbst wenn Sie ignoriert werden). Zitieren seine Dokumentation:Auf Ihre zweite Sorge über die Rückkehr nur eine einzelne Zeile. In die funktionale Programmierung, was Sie versuchen zu tun, ist genannt filtern. Die Stream API stellt eine solche Methode mit Hilfe von
Stream.- filter
. Diese Methode nimmt einePrädikat
als argument, die eine Funktion zurückgibt, dietrue
für alle Elemente, die gehalten werden sollte, undfalse
sonst.In diesem Fall, wir wollen eine
Predicate
zurückgeben würdetrue
wenn der name gleich ist"Neda"
. Dies könnte so geschrieben werden, als den lambda-Ausdrucks -> s.equals("Neda")
.Also im zweiten Teil des Codes, die Sie haben könnten:
Beachten Sie jedoch, dass dies nicht sicherstellen, dass es gibt nur eine Sache, wo der name ist
"Neda"
sammelt alle möglichen Gegenstände in eineList<List<String>>
. Man könnte hinzufügen, eine gewisse Logik zu finden, die das erste Element bzw. löst eine Ausnahme aus, wenn keine Elemente gefunden werden, je nach Ihren geschäftlichen Anforderungen.Beachten Sie noch, dass der Aufruf zweimal
Files.lines(path)
kann vermieden werden, indem direkt eineBufferedReader
wie in @Holger ' s Antwort..filter(
können Sie anrufenfindFirst()
für die Rückgabe des ersten Elements zum Beispiel. Sie habenList<String>
dannBufferedReader
Lesen der ersten Zeile, um zu wissen, wo die zweite Zeile beginnt; es gibt keinen Weg, um dieses.Ich weiß, ich werde Antworten so spät, aber vielleicht hilft es jemand in der Zukunft
Habe ich eine csv-parser/writer , einfach zu bedienen Dank seiner builder pattern
Für Ihren Fall: Sie können filter der Zeilen, die Sie wollen, zu analysieren, mit
Hoffe, Sie finden es nützlich, hier ist die Quellcode
https://github.com/i7paradise/CsvUtils-Java8/
Ich bin seit einer main-Klasse Demo.java zu zeigen, wie es funktioniert