Convertir un tableau bidimensionnel au format csv avec l'API Java 8 Stream

Comment convertir un tableau bidimensionnel au format csv et l'enregistrer dans un fichier à l'aide de l'API Stream de Java 8.

Conversion CSV du tableau de chaînes de caractères

Tout d'abord, un exemple de séparation des chaînes de caractères par des virgules (","). Supposons que vous ayez un tableau à deux dimensions comme celui-ci.

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

cette,

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

Il est produit sous la forme de.

Convertir en liste à l'aide de l'API Stream

Tout d'abord, convertissez chaque ligne en une chaîne séparée par des virgules et stockez-la dans la liste. En effet, il est plus facile de générer des fichiers si vous le définissez sur une liste de chaînes. Puisque vous n'avez qu'à traiter chaque ligne individuellement, diffusez des tableaux avec Arrays.stream () et procédez comme suit.

// 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());

Étant donné que chaque ligne est un tableau de chaînes, [String.join ()](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/String.html#join-java.lang.CharSequence Vous pouvez facilement les combiner en utilisant la méthode -java.lang.CharSequence ...-). String.join () est une méthode ajoutée dans Java 8. Si vous passez un délimiteur et un tableau String, le contenu du tableau sera joint et renvoyé en utilisant le délimiteur spécifié.

Les opérations de terminaison sont répertoriées avec toList () en utilisant collect ().

Liste de sortie dans un fichier

Pour écrire un objet List de String dans un fichier, il est pratique d'utiliser la méthode Files.write () ajoutée à partir de Java 7.

// 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. Passez l'objet Path du fichier de destination d'enregistrement comme premier argument de lang.Iterable-java.nio.file.OpenOption ...-), et l'objet Iteratable qui contient le contenu que vous souhaitez enregistrer comme second argument. Étant donné que la définition du deuxième argument est Iterable <? Extends CharSequence>, le contenu de Iteratable doit être un objet de la classe d'implémentation CharSequence telle que String ou StringBuffer.

Lire les données du fichier et les stocker dans un tableau

Voici un exemple de lecture à partir d'un fichier et de le maintenir dans un tableau à deux dimensions. Cela peut également être traité ligne par ligne, donc [Files.lines ()](https://docs.oracle.com/javase/jp/8/docs/api/java/nio/file/Files.html#lines Il est pratique de lire en utilisant la méthode -java.nio.file.Path-). Files.lines () est une méthode qui lit toutes les lignes du fichier spécifié sous forme de flux. La valeur de retour est 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();
}

Les données lues par Files.lines () ont déjà été diffusées, vous pouvez donc les diviser avec "," et les convertir en tableau pour chaque ligne. J'utilise Stream deux fois, mais l'intérieur est la partie qui traite chaque ligne, et le résultat est un Stream of String []. L'extérieur est la partie qui traite le tout, et comme il s'agit d'un flux de String [] au stade de map (), il peut être transformé en un tableau bidimensionnel avec toArray ().

En plus de cet exemple, [Files.readAllLines ()](https://docs.oracle.com/javase/jp/8/docs/api/java/nio/file/Files.html#readAllLines-java.nio Je pense qu'il est également possible d'utiliser la méthode .file.Path-) pour charger toutes les données dans la liste, puis les traiter.

Conversion CSV d'un tableau bidimensionnel de type primitif

Ensuite, considérons un tableau bidimensionnel de types primitifs. Supposons que vous ayez le tableau suivant:

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

cette

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

Enregistrez-le dans un fichier comme celui-ci.

Convertir en liste à l'aide de l'API Stream

Ceci est également converti en une liste de chaînes à l'aide de l'API Stream, mais comme la méthode String.join () ne peut pas être utilisée pour créer une chaîne séparée par des virgules, vous devez écrire vous-même le traitement de cette partie. Par conséquent, l'intérieur de map () ressemble à ce qui suit.

// 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());

Puisque la cible à traiter est String [], faites-en un Stream puis convertissez chaque élément en un objet String avec mapToObj (). Ensuite, dans l'opération de terminaison, Collectors.joining () est utilisé pour joindre tous les éléments avec ",".

Liste de sortie dans un fichier

La sortie vers le fichier est similaire à celle d'un tableau String.

Lire les données du fichier et les stocker dans un tableau

C'est le même que le cas d'une chaîne de caractères jusqu'au point où il est lu en tant que Stream par la méthode Files.lines (), mais cette fois, je veux le stocker en tant que tableau int, j'ai donc converti chaque élément lu en type int en utilisant mapToInt () dans le traitement de flux Arrangez ci-dessus.

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();
}

Gérer le csv contenant des caractères d'échappement

Le premier exemple était un format csv simple où les chaînes étaient simplement séparées par ",", mais si cela est laissé tel quel, il ne sera pas traité correctement si la chaîne contient des guillemets ou des virgules. Alors, considérons un programme qui peut prendre en charge csv dans lequel chaque champ est entre guillemets.

Il existe différentes bibliothèques pour gérer csv, mais ici je vais l'essayer moi-même. Cependant, il est difficile de l'analyser correctement, nous utilisons donc des expressions régulières pour extraire la chaîne de chaque champ. Par conséquent, nous simplifierons les spécifications csv et supposerons les règles de traitement suivantes.

--Gérer les champs sans guillemets sous forme de chaînes, y compris les espaces

À titre d'exemple, considérons le tableau à deux dimensions suivant.

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 ", ""    }
};

cette

"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 ",""

Il est généré dans un fichier au format suivant.

Convertir en liste à l'aide de l'API Stream

Comme d'habitude, utilisez Stream pour convertir en une chaîne séparée par des virgules et la stocker dans une liste. Cependant, cette fois, j'ajouterai des guillemets doubles aux deux extrémités de chaque chaîne avant de les joindre par des virgules. De plus, les guillemets doubles dans la chaîne doivent être échappés avec "" afin qu'ils soient affichés.

// 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());

Liste de sortie dans un fichier

La sortie vers un fichier est la même que dans les cas précédents.

Pensez à une expression régulière qui correspond à chaque chaîne de champ

Lors de la lecture des données, il est nécessaire de prendre en compte la règle csv mentionnée ci-dessus. Cette fois, nous devons considérer les cas où chaque champ n'est pas entre guillemets et le cas où il est inclus.

S'il n'est pas entouré de guillemets, la ligne entière de la virgule à la virgule (ou du début de la ligne à la virgule, de la virgule à la fin de la ligne) est traitée comme une chaîne de champs.

Si elle est placée entre guillemets, la partie incluse est traitée comme une chaîne de champs. S'il y a une virgule, la virgule fait également partie de la chaîne. Les guillemets doubles sont traités comme des caractères uniquement s'ils sont échappés.

En fonction des conditions ci-dessus, considérez une expression régulière qui correspond à la chaîne de caractères de chaque champ. Tout d'abord, considérons le cas où le caractère d'échappement n'est pas considéré. Cela peut être détecté en utilisant la fonction d'anticipation et d'anticipation, car chaque champ est une virgule à virgule, une ligne commençant à une virgule et une virgule à la fin de la ligne.

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

La partie hogehoge peut être divisée en cas qui ne sont pas entourés de guillemets doubles et en cas qui sont entourés de guillemets doubles. Les observations non fermées peuvent être représentées par des expressions régulières telles que "[^",] * ", et les observations fermées peuvent être représentées par des expressions régulières telles que" "[^"] * "". Le fait est que le premier ne permet pas ",", tandis que le second le permet. Si vous écrivez ceci ensemble, ce sera comme suit.

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

Si vous ajoutez la perspective et la recherche en arrière à cela, ce sera comme suit.

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

Maintenant,""Lorsque vous envisagez de vous échapper, n'importe quel caractère peut venir après le caractère d'échappement, donc le précédent "[^",]"" Est "(?:\.|[^\",])"On dirait. De même, ""[^"]""" Est ""(?:\.|[^\"])""On dirait. Dans cet esprit, l'expression régulière finale peut être écrite comme suit:

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

Lire les données du fichier et les stocker dans un tableau

Vous pouvez utiliser cette expression canonique pour récupérer la valeur de chaque champ individuellement lorsque vous lisez les données du fichier.

// 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();
}

Tout d'abord, créez un objet Pattern d'expression régulière et compilez-le. Obtenir des données d'un fichier sous forme de flux et les traiter ligne par ligne est la même chose qu'auparavant. Dans la partie qui traite chaque ligne, nous utiliserons Matcher pour extraire la chaîne de caractères correspondante. Notez que dans cet exemple, les champs qui ne correspondent pas (ne respectent pas les règles) sont ignorés.

La chaîne de caractères correspondante est temporairement stockée dans List, mais à ce moment-là, les guillemets avant et après sont supprimés. Enfin, le tableau avec toArray () est le même qu'avant.

Si vous concevez le traitement de la partie expression régulière, vous pouvez gérer la lecture de conditions plus compliquées.

Recommended Posts

Convertir un tableau bidimensionnel au format csv avec l'API Java 8 Stream
Comment convertir un tableau de chaînes en un tableau d'objets avec l'API Stream
[Java] Introduction à l'API Stream
[Java] Convertir ArrayList en tableau
[java8] Pour comprendre l'API Stream
[Introduction à Java] À propos de l'API Stream
[Java] Convertit un tableau en ArrayList * Attention
Convertir le tableau 2D de Swift en tableau 2D de C
API Java Stream
Java8 / 9 Beginners: Streaming API addiction points et comment les gérer
Convertissez une chaîne en un tableau caractère par caractère avec Swift
Comment utiliser l'API Java avec des expressions lambda
[Java] API / carte de flux
Pratique de l'API Java8 Stream
Traitement des listes à comprendre avec des images - java8 stream / javaslang-
Exemple de code pour convertir List en List <String> dans Java Stream
Convertissez de gros fichiers XLSX en CSV avec Apache POI
Tableau 2D AtCoder ABC129 D résolu en Ruby et Java
Comment convertir un fichier en tableau d'octets en Java
Je souhaite renvoyer un objet au format CSV avec en-tête et filtre multilignes en Java
[Java] Je souhaite convertir un tableau d'octets en un nombre hexadécimal
Java pour jouer avec Function
[À voir absolument pour l'apprenti ingénieur Java] Comment utiliser l'API Stream
Aide-mémoire de l'API Java Stream
Convertir un tableau d'octets Java en une chaîne hexadécimale
Je veux écrire une boucle qui fait référence à un index avec l'API Stream de Java 8
Gérez les exceptions avec fraîcheur avec les expressions lambda Java 8 et l'API Stream
API Java Stream en 5 minutes
[Java] Stream API - Traitement de l'arrêt du flux
[Java] Stream API - Traitement intermédiaire de flux
Connectez-vous à DB avec Java
Connectez-vous à MySQL 8 avec Java
Traitement des listes à comprendre avec des images --java8 stream / javaslang --bonus
Comment convertir la base Java
Il est maintenant temps de commencer avec l'API Stream
Pour les débutants Java: List, Map, Iterator / Array ... Comment convertir?
Convertir un tableau potentiellement nul en flux
Comment initialiser un tableau Java
[Java] Opération intermédiaire de l'API Stream
Source pour afficher le tableau de caractères avec numberPicker dans Android Studio (Java)
Modifiez SVG avec Java + Apache Batik et convertissez-le en PNG ou JPEG
[Java 8] Suppression en double (et vérification en double) avec Stream
Java pour apprendre avec les ramen [Partie 1]
Convertir un objet ruby au format JSON
[Java] Points à noter avec Arrays.asList ()
Convertissez Markdown en HTML avec flexmark-java
Osez défier Kaggle avec Java (1)
[Java] Vérification de l'existence des éléments avec Stream
[Java] Convertir 1 en N liste en carte
J'ai essayé d'utiliser l'API Java8 Stream
Java, des tableaux pour débuter avec les débutants
[Android] Convertir le code Java Android en Kotlin
[Java] Conversion d'un tableau à une liste
Comment créer un tableau Java
[Petite histoire] Convertir Stream en Iterable
Liste de conversion mutuelle de tableau / liste / flux Java
Conversion de liste Java8 avec Stream map
Je voulais écrire un processus équivalent à une instruction while avec l'API Java 8 Stream