Konvertieren Sie ein zweidimensionales Array mit der Java 8 Stream-API in das CSV-Format

So konvertieren Sie ein zweidimensionales Array in das CSV-Format und speichern es mithilfe der Stream-API von Java 8 in einer Datei.

CSV-Konvertierung des Zeichenfolgenarrays

Zunächst ein Beispiel für das einfache Trennen von Zeichenfolgen durch Kommas (","). Angenommen, Sie haben ein zweidimensionales Array wie dieses.

String arrays[][] = {
    { "aaa", "bbb", "ccc", "ddd", "eee" },
    { "abc", "def", "hij", "klm", "opq" },
    { "AAA", "BBB", "CCC", "DDD", "EEE" }
};

Dies,

aaa,bbb,ccc,ddd,eee
abc,def,hij,klm,opq
AAA,BBB,CCC,DDD,EEE

Es wird in Form von ausgegeben.

Mit Stream-API in Liste konvertieren

Konvertieren Sie zunächst jede Zeile in eine durch Kommas getrennte Zeichenfolge und speichern Sie sie in der Liste. Dies liegt daran, dass es einfacher ist, Dateien auszugeben, wenn Sie eine Liste von Zeichenfolgen festlegen. Da Sie nur jede Zeile einzeln verarbeiten müssen, streamen Sie Arrays mit Arrays.stream () und verarbeiten Sie sie wie folgt.

// convert each array[] to csv strings, then store to a List
List<String> list = Arrays.stream(arrays)
                .map(line -> String.join(",",line))
                .collect(Collectors.toList());

Da jede Zeile ein Array von Strings ist, String.join () Sie können sie einfach mit der Methode -java.lang.CharSequence ...-) kombinieren. String.join () ist eine in Java 8 hinzugefügte Methode. Wenn Sie ein Trennzeichen und ein String-Array übergeben, wird der Inhalt des Arrays mit dem angegebenen Trennzeichen verknüpft und zurückgegeben.

Beendigungsvorgänge werden mit toList () unter Verwendung von collect () aufgelistet.

Ausgabeliste in Datei

Um ein List-Objekt von String in eine Datei zu schreiben, können Sie die von Java 7 hinzugefügte Methode Files.write () verwenden.

// save to a file on current dir
try {
  
  Files.write(Paths.get(System.getProperty("user.dir"),"out.csv"), list, StandardOpenOption.CREATE);
} catch (IOException e) {
    e.printStackTrace();
}

[Files.write ()](https://docs.oracle.com/javase/jp/8/docs/api/java/nio/file/Files.html#write-java.nio.file.Path-java. Übergeben Sie das Path-Objekt der Speicherzieldatei als erstes Argument von lang.Iterable-java.nio.file.OpenOption ...-) und das Iteratable-Objekt, das den Inhalt enthält, den Sie als zweites Argument speichern möchten. Da die Definition des zweiten Arguments Iterable <? Extends CharSequence> lautet, muss der Inhalt von Iteratable ein Objekt der CharSequence-Implementierungsklasse wie String oder StringBuffer sein.

Lesen Sie Daten aus der Datei und speichern Sie sie im Array

Das Folgende ist ein Beispiel für das Lesen aus einer Datei und das Halten in einem zweidimensionalen Array. Dies kann auch zeilenweise verarbeitet werden, also Files.lines () Es ist bequem, mit der Methode -java.nio.file.Path-) zu lesen. Files.lines () ist eine Methode, die alle Zeilen aus der angegebenen Datei als Stream liest. Der Rückgabewert ist Stream .

// read from csv file
try (Stream<String> stream = Files.lines(Paths.get(System.getProperty("user.dir"),"out.csv"))) {
    // read each line
    String data[][] = stream.map(line -> line.split(","))
            .map(line -> Arrays.stream(line)
                    .map(String::new)
                    .toArray(String[]::new))
            .toArray(String[][]::new);
} catch (IOException e) {
    e.printStackTrace();
}

Die von Files.lines () gelesenen Daten wurden bereits gestreamt, sodass Sie sie mit "," teilen und für jede Zeile in ein Array konvertieren können. Ich verwende Stream zweimal, aber das Innere ist der Teil, der jede Zeile verarbeitet, und das Ergebnis ist ein Stream von String []. Das Äußere ist der Teil, der das Ganze verarbeitet, und da es sich im Stadium von map () um einen Stream von String [] handelt, kann es mit toArray () zu einem zweidimensionalen Array gemacht werden.

Zusätzlich zu diesem Beispiel [Files.readAllLines ()](https://docs.oracle.com/javase/jp/8/docs/api/java/nio/file/Files.html#readAllLines-java.nio Ich denke, es ist auch möglich, die .file.Path-) Methode zu verwenden, um alle Daten in die Liste zu laden und sie dann zu verarbeiten.

CSV-Umwandlung eines zweidimensionalen Arrays vom primitiven Typ

Betrachten Sie als nächstes ein zweidimensionales Array primitiver Typen. Angenommen, Sie haben das folgende Array:

int arrays[][] = {
    { 11, 12, 13, 14, 15 },
    { 21, 22, 23, 24, 25 },
    { 31, 32, 33, 34, 35 }
};

Dies

11,12,13,14,15
21,22,23,24,25
31,32,33,34,35

Speichern Sie es in einer Datei wie dieser.

Mit Stream-API in Liste konvertieren

Dies wird auch mithilfe der Stream-API in eine Zeichenfolgenliste konvertiert. Da die String.join () -Methode jedoch nicht zum Erstellen einer durch Kommas getrennten Zeichenfolge verwendet werden kann, müssen Sie die Verarbeitung für diesen Teil selbst schreiben. Daher sieht das Innere von map () wie folgt aus.

// convert each array[] to csv strings, then store to a List
List<String> list = Arrays.stream(arrays)
        .map(line -> Arrays.stream(line)
                .mapToObj(String::valueOf)
                .collect(Collectors.joining(",")))
        .collect(Collectors.toList());

Da das zu verarbeitende Ziel String [] ist, machen Sie dies zu einem Stream und konvertieren Sie dann jedes Element mit mapToObj () in ein String-Objekt. In der Beendigungsoperation wird dann Collectors.joining () verwendet, um alle Elemente mit "," zu verbinden.

Ausgabeliste in Datei

Die Ausgabe in die Datei ähnelt der eines String-Arrays.

Lesen Sie Daten aus der Datei und speichern Sie sie im Array

Es ist dasselbe wie bei einer Zeichenfolge bis zu dem Punkt, an dem sie von der Files.lines () -Methode als Stream gelesen wird, aber dieses Mal möchte ich sie als int-Array speichern, also habe ich jedes Leseelement mit mapToInt () in der Stream-Verarbeitung in den Typ int konvertiert Oben anordnen.

try (Stream<String> stream = Files.lines(Paths.get(System.getProperty("user.dir"),"out2.csv"))) {
    // read each line
    int data[][] = stream.map(line -> line.split(","))
            .map(line -> Arrays.stream(line)
                    .mapToInt(Integer::parseInt)
                    .toArray())
            .toArray(int[][]::new);
    } catch (IOException e) {
        e.printStackTrace();
}

Behandeln Sie CSV mit Escape-Zeichen

Das erste Beispiel war ein einfaches CSV-Format, bei dem die Zeichenfolgen einfach durch "," getrennt wurden. Wenn dies jedoch unverändert bleibt, wird es nicht korrekt verarbeitet, wenn die Zeichenfolge doppelte Anführungszeichen oder Kommas enthält. Betrachten wir also ein Programm, das CSV unterstützen kann, in dem jedes Feld in doppelte Anführungszeichen eingeschlossen ist.

Es gibt verschiedene Bibliotheken für den Umgang mit CSV, aber hier werde ich es selbst versuchen. Es ist jedoch schwierig, es richtig zu analysieren, daher verwenden wir reguläre Ausdrücke, um die Zeichenfolge für jedes Feld zu extrahieren. Daher werden wir die CSV-Spezifikationen vereinfachen und die folgenden Regeln für die Verarbeitung übernehmen.

Betrachten Sie als Beispiel das folgende zweidimensionale Array.

String[][] arrays = {
        { "Dog" , "Cat"   , ""         , "Turtle", ""     , ""    },
        { "hoge", "pi yo" , " fuga "   , "  foo" , "bar  ", "bow" },
        { "hoge", " pi yo", " fuga "   , "foo "  , "bar " , ""    },
        { "hoge", "pi yo" , "fu\" ga", "foo"   , "bar " , "bow" },
        { "    ", "pi yo" , "fu,ga ", "foo"   , " bar ", ""    }
};

Dies

"Dog","Cat","","Turtle","",""
"hoge","pi yo"," fuga ","  foo","bar  ","bow"
"hoge"," pi yo"," fuga ","foo ","bar ",""
"hoge","pi yo","fu\" ga","foo","bar ","bow"
"    ","pi yo","fu,ga ","foo"," bar ",""

Es wird in einer Datei im folgenden Format ausgegeben.

Mit Stream-API in Liste konvertieren

Verwenden Sie Stream wie gewohnt, um eine durch Kommas getrennte Zeichenfolge zu konvertieren und in einer Liste zu speichern. Dieses Mal füge ich jedoch an beiden Enden jeder Zeichenfolge doppelte Anführungszeichen hinzu, bevor ich sie mit Kommas verbinde. Außerdem sollten doppelte Anführungszeichen in der Zeichenfolge mit "" maskiert werden, damit sie ausgegeben werden.

// convert each array[] to csv strings, then store to a List
List<String> list = Arrays.stream(arrays)
        .map(line -> Arrays.stream(line)
                .map(str -> str.replaceAll("\\\"", "\\\\\""))
                .map(str -> "\"" + str + "\"")
                .collect(Collectors.joining(",")))
        .collect(Collectors.toList());

Ausgabeliste in Datei

Die Ausgabe in eine Datei ist dieselbe wie in früheren Fällen.

Stellen Sie sich einen regulären Ausdruck vor, der mit jeder Feldzeichenfolge übereinstimmt

Beim Lesen der Daten muss die oben genannte CSV-Regel berücksichtigt werden. Dieses Mal müssen wir die Fälle berücksichtigen, in denen jedes Feld nicht in doppelte Anführungszeichen eingeschlossen ist, und den Fall, in dem es eingeschlossen ist.

Wenn es nicht in doppelte Anführungszeichen eingeschlossen ist, wird die gesamte Zeile von Komma zu Komma (oder vom Anfang der Zeile zum Komma, vom Komma zum Ende der Zeile) als eine Folge von Feldern behandelt.

Wenn es in doppelte Anführungszeichen eingeschlossen ist, wird der eingeschlossene Teil als eine Folge von Feldern behandelt. Wenn ein Komma darin ist, ist das Komma auch Teil der Zeichenfolge. Doppelte Anführungszeichen werden nur dann als Zeichen behandelt, wenn sie maskiert sind.

Betrachten Sie basierend auf den obigen Bedingungen einen regulären Ausdruck, der mit der Zeichenfolge jedes Felds übereinstimmt. Betrachten Sie zunächst den Fall, in dem das Escape-Zeichen nicht berücksichtigt wird. Dies kann mithilfe von Look-Ahead und Look-Behind erkannt werden, da jedes Feld Komma-Komma, Zeilenanfang bis Komma und Komma bis Zeilenende ist.

(?<=^|,)hogehoge(?=$|,)

Der Hogehoge-Teil kann in Fälle unterteilt werden, die nicht von doppelten Anführungszeichen umgeben sind, und Fälle, die von doppelten Anführungszeichen umgeben sind. Nicht geschlossene Fälle können durch reguläre Ausdrücke wie "[^",] * "dargestellt werden, und eingeschlossene Fälle können durch reguläre Ausdrücke wie" "[^"] * "" dargestellt werden. Der Punkt ist, dass das erstere nicht "," erlaubt, während das letztere es erlaubt. Wenn Sie dies zusammen schreiben, wird es wie folgt sein.

(?:[^",]*|"[^"]*")

Wenn Sie den Look-Ahead und den Look-Behind hinzufügen, ist dies wie folgt.

(?<=^|,)(?:[^",]*|"[^"]*")(?=$|,)

Jetzt,""Wenn Sie überlegen, ob Sie vorbeikommen möchten, kann jedes Zeichen nach dem Fluchtzeichen kommen, also das vorherige "[^",]"" Ist "(?:\.|[^\",])"Es sieht aus wie. Ebenso ""[^"]""" Ist ""(?:\.|[^\"])""Es sieht aus wie. In diesem Sinne kann der endgültige reguläre Ausdruck wie folgt geschrieben werden:

(?<=^|,)(?:(?:\\.|[^\\",])*|"(?:\\.|[^\\"])*")(?=$|,)

Lesen Sie Daten aus der Datei und speichern Sie sie im Array

Mit diesem kanonischen Ausdruck können Sie den Wert jedes Felds einzeln abrufen, während Sie die Daten aus der Datei lesen.

// Regex expression that matches with csv fields.
String regex = "(?<=^|,)(?:(?:\\\\.|[^\\\\\",])*|\"(?:\\\\.|[^\\\\\"])*\")(?=$|,)";
Pattern pattern = Pattern.compile(regex);

// open a file
try (Stream<String> stream = Files.lines(Paths.get(System.getProperty("user.dir"),"out3.csv"))) {
    // read each line
    String data[][] = stream
            .map(line -> {
                    Matcher matcher = pattern.matcher(line);
                    List<String> aList = new ArrayList<>();
                    while (matcher.find()) {
                    
  
  aList.add(matcher.group().replaceFirst("^\"","").replaceFirst("\"$",""));
                    }
                            return aList;
                })
            .map(line -> line.stream().toArray(String[]::new))
            .toArray(String[][]::new);
} catch (IOException e) {
    e.printStackTrace();
}

Erstellen Sie zunächst ein Musterobjekt mit regulärem Ausdruck und kompilieren Sie es. Das Abrufen und zeilenweise Verarbeiten von Daten aus einer Datei als Stream ist das gleiche wie zuvor. In dem Teil, der jede Zeile verarbeitet, verwenden wir Matcher, um die übereinstimmende Zeichenfolge zu extrahieren. Beachten Sie, dass in diesem Beispiel Felder, die nicht übereinstimmen (nicht den Regeln folgen), ignoriert werden.

Die übereinstimmende Zeichenfolge wird vorübergehend in List gespeichert, aber zu diesem Zeitpunkt werden die doppelten Anführungszeichen davor und danach entfernt. Schließlich ist die Anordnung mit toArray () dieselbe wie zuvor.

Wenn Sie die Verarbeitung des Teils für reguläre Ausdrücke planen, können Sie kompliziertere Bedingungen lesen.

Recommended Posts

Konvertieren Sie ein zweidimensionales Array mit der Java 8 Stream-API in das CSV-Format
So konvertieren Sie ein Array von Strings mit der Stream-API in ein Array von Objekten
[Java] Einführung in die Stream-API
[Java] Konvertiert ArrayList in Array
[java8] Um die Stream-API zu verstehen
[Einführung in Java] Informationen zur Stream-API
[Java] Konvertieren Sie ein Array in ArrayList * Achtung
Konvertieren Sie das 2D-Array von Swift in das 2D-Array von C.
Java Stream API
Java8 / 9-Anfänger: Streamen Sie API-Suchtpunkte und wie Sie damit umgehen
Konvertieren Sie eine Zeichenfolge mit swift in ein zeichenweises Array
Verwendung der Java-API mit Lambda-Ausdrücken
[Java] Stream API / Map
Listenverarbeitung zum Verstehen mit Bildern --java8 stream / javaslang-
Beispielcode zum Konvertieren von List in List <String> in Java Stream
Konvertieren Sie große XLSX-Dateien mit Apache POI in CSV
AtCoder ABC129 D 2D-Array In Ruby und Java gelöst
So konvertieren Sie eine Datei in ein Byte-Array in Java
Ich möchte ein Objekt im CSV-Format mit mehrzeiligem Header und Filter in Java zurückgeben
[Java] Ich möchte ein Byte-Array in eine Hexadezimalzahl konvertieren
Java zum Spielen mit Function
[Ein Muss für einen Java-Ingenieurlehrling] Verwendung der Stream-API
Java Stream API Spickzettel
Konvertieren Sie ein Java-Byte-Array in eine hexadezimale Zeichenfolge
Ich möchte eine Schleife schreiben, die auf einen Index mit der Stream-API von Java 8 verweist
Behandeln Sie Ausnahmen kühl mit Java 8-Lambda-Ausdrücken und der Stream-API
Java Stream API in 5 Minuten
[Java] Stream API - Stream-Beendigungsverarbeitung
[Java] Stream API - Stream Zwischenverarbeitung
Stellen Sie mit Java eine Verbindung zur Datenbank her
Stellen Sie mit Java eine Verbindung zu MySQL 8 her
Listenverarbeitung zum Verstehen mit Bildern --java8 stream / javaslang --bonus
So konvertieren Sie Java Base
Jetzt ist es an der Zeit, mit der Stream-API zu beginnen
Für Java-Anfänger: Liste, Karte, Iterator / Array ... Wie konvertiere ich?
Konvertieren Sie ein potenziell null-Array in einen Stream
So initialisieren Sie ein Java-Array
[Java] Stream API Zwischenoperation
Quelle zur Anzeige des Zeichenarrays mit numberPicker in Android Studio (Java)
Bearbeiten Sie SVG mit Java + Apache Batik und konvertieren Sie es in PNG oder JPEG
[Java 8] Doppelte Löschung (& doppelte Überprüfung) mit Stream
Java mit Ramen lernen [Teil 1]
Konvertieren Sie das Ruby-Objekt in das JSON-Format
[Java] Mit Arrays.asList () zu beachtende Punkte
Konvertieren Sie Markdown mit flexmark-java in HTML
Wagen Sie es, Kaggle mit Java herauszufordern (1)
[Java] Elementexistenzprüfung mit Stream
[Java] Konvertiere 1 in N Liste in Karte
Ich habe versucht, die Java8 Stream API zu verwenden
Java, Arrays für Anfänger
[Android] Konvertieren Sie Android Java-Code in Kotlin
[Java] Konvertierung von Array zu Liste
So erstellen Sie ein Java-Array
[Kleine Geschichte] Stream in Iterable konvertieren
Java-Array / Liste / Stream gegenseitige Konvertierungsliste
Java8-Listenkonvertierung mit Stream Map
Eine Geschichte, die ich mit der Stream-API von Java8 einem Prozess schreiben wollte, der einer while-Anweisung entspricht