Lire CSV avec Scanner()
Mon csv est lu dans le système.out, mais j'ai remarqué que tout texte avec un espace est déplacé dans la ligne suivante (comme un retour n)
Voici comment mon csv commence:
first,last,email,address 1, address 2
john,smith,blah@blah.com,123 St. Street,
Jane,Smith,blech@blech.com,4455 Roger Cir,apt 2
Après avoir exécuté mon application, toute cellule avec un espace (Adresse 1) est jetée sur la ligne suivante.
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class main {
public static void main(String[] args) {
// -define .csv file in app
String fileNameDefined = "uploadedcsv/employees.csv";
// -File class needed to turn stringName to actual file
File file = new File(fileNameDefined);
try{
// -read from filePooped with Scanner class
Scanner inputStream = new Scanner(file);
// hashNext() loops line-by-line
while(inputStream.hasNext()){
//read single line, put in string
String data = inputStream.next();
System.out.println(data + "***");
}
// after loop, close scanner
inputStream.close();
}catch (FileNotFoundException e){
e.printStackTrace();
}
}
}
Voici donc le résultat dans la console:
first,last,email,address 1,address 2 john,smith,blah@blah.com,123 St. Street, Jane,Smith,blech@blech.com,4455 Roger Cir,apt 2
Est - ce que J'utilise le Scanner de manière incorrecte?
7 réponses
scanner.useDelimiter(",");
Cela devrait fonctionner.
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class TestScanner {
public static void main(String[] args) throws FileNotFoundException {
Scanner scanner = new Scanner(new File("/Users/pankaj/abc.csv"));
scanner.useDelimiter(",");
while(scanner.hasNext()){
System.out.print(scanner.next()+"|");
}
scanner.close();
}
}
Pour le fichier CSV:
a,b,c d,e
1,2,3 4,5
X,Y,Z A,B
La sortie est:
a|b|c d|e
1|2|3 4|5
X|Y|Z A|B|
Veuillez arrêter d'écrire des analyseurs CSV défectueux!
J'ai vu des centaines D'analyseurs CSV et soi-disant tutoriels pour eux en ligne.
Presque chacun d'entre eux se trompe!
, Ce ne serait pas une si mauvaise chose que ça n'affecte pas moi, mais les gens qui essaient d'écrire CSV lecteurs et se tromper tendance à écrire CSV écrivains, trop. Et de les faire de mal que de bien. Et ceux-ci pour lesquels je dois écrire des analyseurs.
, Veuillez garder à l'esprit que CSV (par ordre croissant pas si évident):
- peut avoir des caractères de citation autour des valeurs
- peut avoir d'autres caractères de citation que "
- peut même avoir d'autres caractères de citation que " et '
- ne peut avoir aucun caractère de citation
- peut même avoir des caractères de citation sur certaines valeurs et aucun sur d'autres
- peut avoir d'autres séparateurs que, et;
- peut avoir des espaces entre les séparateurs et les valeurs (entre guillemets)
- peut avoir autres jeux de caractères que ascii
- devrait avoir le même nombre de valeurs dans chaque ligne, mais pas toujours
- peut contenir des champs vides, entre guillemets:
"foo","","bar"
ou non:"foo",,"bar"
- peut contenir des retours à la ligne dans les valeurs
- ne peut pas contenir des nouvelles lignes dans les valeurs si elles ne sont pas délimitées
- ne peut pas contenir des sauts de ligne entre les valeurs
- peut avoir le caractère délimitant dans la valeur s'il est correctement échappé
- fait pas utiliser backslash pour échapper délimiteurs mais...
- utilise le caractère de citation lui-même pour l'échapper, par exemple {[2] } sera
'Frodo''s Ring'
- peut avoir le caractère de citation au début ou à la fin de la valeur, ou même comme seul caractère (
"foo""", """bar", """"
) - peut même avoir le caractère entre guillemets dans la valeur non entre guillemets; celui-ci est pas échappé
Si vous pensez que cela est évident pas un problème, alors détrompez-vous. J'ai vu chacun de ces éléments mis en œuvre à tort. Même dans les logiciels majeurs . (par exemple, les suites bureautiques, les systèmes CRM)
Il existe de bons lecteurs et écrivains CSV prêts à l'emploi:
Si vous insistez pour écrire le vôtre, lisez au moins le (très court) RFC pour CSV .
Scanner.next()
ne lit pas de nouvelle ligne mais lit le jeton suivant, délimité par des espaces (par défaut, si useDelimiter()
n'a pas été utilisé pour changer le motif de délimiteur). Pour lire une ligne, Utilisez Scanner.nextLine()
.
Une fois que vous avez lu une seule ligne, vous pouvez utiliser String.split(",")
pour séparer la ligne en champs. Cela permet d'identifier les lignes qui ne sont pas constituées du nombre requis de champs. L'utilisation de useDelimiter(",");
ignorerait la structure en ligne du fichier (chaque ligne consiste en une liste de champs séparés par une virgule). Pour exemple:
while (inputStream.hasNextLine())
{
String line = inputStream.nextLine();
String[] fields = line.split(",");
if (fields.length >= 4) // At least one address specified.
{
for (String field: fields) System.out.print(field + "|");
System.out.println();
}
else
{
System.err.println("Invalid record: " + line);
}
}
Comme déjà mentionné, l'utilisation D'une bibliothèque CSV est recommandée. D'une part, cette solution (et useDelimiter(",")
) ne gérera pas correctement les identifiants entre guillemets contenant des caractères ,
.
Je suis d'accord avec Scheintod que l'utilisation d'une bibliothèque CSV existante est une bonne idée d'avoir la conformité RFC-4180 dès le début. Outre les opencsv et Oster Miller mentionnés, il existe une série d'autres bibliothèques CSV. Si vous êtes intéressé par les performances, vous pouvez jeter un oeil à la uniVocity/csv-parsers-comparison. Il montre que
Sont toujours les plus rapides en utilisant JDK 6, 7, 8 ou 9. L'étude n'a trouvé aucun problème de compatibilité RFC 4180 dans l'un de ces trois. OpenCSV et Oster Miller sont deux fois plus lents que ceux-ci.
Je ne suis en aucune façon associé au(X) auteur (s), mais en ce qui concerne l'analyseur csv uniVocity, l'étude pourrait être biaisée en raison du fait que son auteur est le même que celui de cet analyseur.
À noter, L'auteur de SimpleFlatMapper a également publié un comparaison de performance comparant seulement ces trois.
Diviser nextLine() par ce délimiteur - (?=([^\"]\"[^\"]\")[^\"]$)") pour un tableau.
Il gère votre problème
Si vous devez absolument utiliser Scanner, vous devez définir son délimiteur via sa méthode useDelimiter(...)
. Sinon, il utilisera par défaut tous les espaces blancs comme délimiteur. Mieux encore, comme cela a déjà été dit-utilisez une bibliothèque CSV car c'est ce qu'ils font le mieux.
Par exemple, ce délimiteur sera divisé sur des virgules Avec ou sans espaces environnants:
scanner.useDelimiter("\\s*,\\s*");
Veuillez consulter le java.util.API du Scanner pour en savoir plus.
Eh bien, je fais mon codage dans NetBeans 8.1:
Tout d'abord: créez un nouveau projet, sélectionnez Application Java et nommez votre projet.
Puis modifiez votre code après la classe publique pour ressembler à ce qui suit:
/**
* @param args the command line arguments
* @throws java.io.FileNotFoundException
*/
public static void main(String[] args) throws FileNotFoundException {
try (Scanner scanner = new Scanner(new File("C:\\Users\\YourName\\Folder\\file.csv"))) {
scanner.useDelimiter(",");
while(scanner.hasNext()){
System.out.print(scanner.next()+"|");
}}
}
}