Comment convertir un tableau bidimensionnel au format csv et l'enregistrer dans un fichier à l'aide de l'API Stream de Java 8.
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.
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 ().
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.
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.
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.
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 ",".
La sortie vers le fichier est similaire à celle d'un tableau String.
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();
}
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.
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());
La sortie vers un fichier est la même que dans les cas précédents.
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:
(?<=^|,)(?:(?:\\.|[^\\",])*|"(?:\\.|[^\\"])*")(?=$|,)
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