Charger des données depuis un fichier CSV simple en 5 minutes

Thierry Leriche-Dessirier

Ce petit article montre (par l'exemple) comment charger des données depuis un fichier CSV simple, à l'aide d'Open-CSV (et MoteurCsv en annexe), en quelques minutes. Commentez ♪

- Titre : Charger des données depuis un fichier CSV simple en 5 minutes

- Auteur : Thierry Leriche-Dessirier

- Parution : 26 janvier 2012

- Mise à jour : 12 juillet 2012

- Public visé :

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2011 Thierry Leriche-Dessirier. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- 1. Introduction

- 1-A. À propos

- 1-B. Avant de commencer

- 1-C. Mise à jour

- 2. Découverte du modèle d'exemple

- 2-A. Télécharger, installer et importer le projet d'exemple

- 2-B. Les objets du modèle

- 2-C. Les données à lire

- 3. Action

- 3-A. Compléter le modèle

- 3-B. Créer des DAO vides

- 3-C. Ajout d'Open-CSV

- 3-D. Lecture des données

- 4. Conclusions

- 5. Remerciements

- 6. Annexes

- 6-A. Liens

- 6-B. Les fichiers importants en entier

- 6-C. Utilisation de la lib MoteurCsv

- 6-C-1. Version 1.2.0 de la lib

1 - Introduction

Dans ce document, nous allons voir comment charger des données depuis un fichier CSV simple à l'aide de la bibliothèque Open-CSV (et MoteurCsv en annexe), puis transformer ces données en objets de notre modèle.

Open-CSV est une bibliothèque gratuite, écrite en Java, qui simplifie la lecture complexe des fichiers CSV.

1-A - À propos

Découvrir une technologie n'est pas chose facile. En aborder plusieurs d'un coup l'est encore moins. Partant de ce constat, cet article a été écrit pour aller à l'essentiel. Les points importants sont présentés dans le corps de l'article et les éléments secondaires sont expliqués en annexe.

1-B - Avant de commencer

Pour écrire ce tutoriel, j'ai utilisé les éléments suivants :

- Java JDK 1.6.0_24-b07 ;

- Eclipse Indigo 3.7 JEE 64b ;

- Maven 3.0.3.

Ce tutoriel s'inspire très largement de mon article sur la lecture des fichiers CSV en Java. Ce tutoriel reprend également les objets utilisés dans les articles « Afficher un tableau avec un Table Model Swing en 5 minutes » et « Afficher un graphe jfreechart en 5 minutes ». Même si elle est conseillée, la lecture de ces articles n'est pas indispensable pour la suite.

1-C - Mise à jour

27 février 2012 : Ajout d'un exemple d'utilisation de la lib MoteurCsv (en annexe).

12 juillet 2012 : Prise en compte de la nouvelle version de la lib MoteurCSV (en annexe).

2 - Découverte du modèle d'exemple

2-A - Télécharger, installer et importer le projet d'exemple

Pour commencer, je vous propose de télécharger le fichier Zip « notes3_domain.zip », contenant un projet Java-Maven d'exemple.

Compilez le projet d'exemple et importez-le dans Eclipse (comme expliqué dans le tutoriel « Importer un projet Maven dans Eclipse en 5 minutes ») ou dans l'IDE de votre choix.

Pour suivre ce tutoriel, vous pouvez vous contenter de lire les codes proposés ci-dessous (codes complets en annexe).

2-B - Les objets du modèle

Le projet d'exemple reprend les objets utilisés dans les tutoriels cités plus haut, et dont l'objectif est de modéliser (et d'afficher) les notes d'un groupe d'élèves au dernier examen.

Sans ordre, le modèle est composé des objets suivants.

Sexe.java

package com.thi.notes.domain;

public enum Sexe {

HOMME("Garçon"),
FEMME("Fille");

private final String label;

Sexe(String label) {
this.label = label;
}

public String getLabel() {
return label;
}
}

Eleve.java

package com.thi.notes.domain;

public class Eleve {

private String nom;
private String prenom;
private Integer annee;
private Sexe sexe;

public Eleve(String nom, String prenom, Integer annee, Sexe sexe) {
...
}

// + getter-setter
}

NoteEleve.java

package com.thi.notes.domain;

public class NoteEleve {

private Eleve eleve;
private Double note;

public NoteEleve(Eleve eleve, Double note) {
...
}

// + getter-setter
}

Sans surprise, la classe Eleve représente une personne, et plus spécifiquement un élève. La classe NoteEleve est un « wrapper » qui possède un objet Eleve et une note (sur 20) associée. Quant à l'enum Sexe, son rôle dans l'histoire est évident.

2-C - Les données à lire

Dans ce tutoriel, nous allons utiliser deux fichiers CSV. Le premier, nommé « eleves.csv », contient la liste des élèves. Le second, nommé « notes-dernier-exam.csv », contient quant à lui les notes desdits élèves au dernier examen.

Dans un fichier CSV, les données sont séparées par des virgules, ou un point-virgule pour le format CSV français standard.

eleves.csv

# Nom;Prénom;Classe;Sexe;Date de naissance;Adresse

Durand;Marie;3;F;02/01/1992;15 rue du Lac 75001 Paris
Alesi;Julie;3;F;08/01/1992;72 av. Jean Dupont 75003 Paris
Martini;Carine;3;F;17/01/1992;2 rue du Moulin 92230 Neullavy
Varola;Sophie;3;F;21/01/1992;15 rue du Lac 75001 Paris
Labiche;Lelou;3;F;21/01/1992;15 rue du Lac 75001 Paris
Dujardin;Anne;3;F;03/02/1992;67 rue des Jardins 91800 Brunoy
Laventure;Martine;3;F;15/02/1992;15 rue du Lac 75001 Paris
...
Fivolini;Kevin;3;H;06/12/1992;15 rue du Lac 75001 Paris
Laferme;Martin;3;H;07/12/1992;15 rue du Lac 75001 Paris
Dupuis;Vincent;3;H;15/12/1992;15 rue du Lac 75001 Paris
Lagrange;Alexandre;3;H;28/12/1992;15 rue du Lac 75001 Paris

On constate que de nombreux élèves habitent au « 15 rue du Lac ». Cette adresse est celle de la résidence universitaire de l'école.

notes-dernier-exam.csv

# Nom;Prénom;Note

Durand;Marie;5
Alesi;Julie;8
Martini;Carine;10,5
Varola;Sophie;12
Labiche;Lelou;12
Dujardin;Anne;13
Laventure;Martine;14
...
Fivolini;Kevin;19
Laferme;Martin;20
Dupuis;Vincent;20

On remarque que « Alexandre Lagrange », que j'ai mis en fin de fichier pour simplifier la lecture, n'a pas de note au dernier examen ; il était absent. Dans ce tutoriel, nous dirons qu'une absence vaut un zéro. Il faudra donc gérer ce cas.

3 - Action

3-A - Compléter le modèle

Durée estimée : 30 secondes.

Pour commencer, on doit ajouter les attributs « date de naissance » et « adresse », qui ne sont pas encore dans « Eleve.java » et modifier le constructeur de la classe en conséquence.

Nouveaux attributs dans Eleve.java

public class Eleve {

private String nom;
private String prenom;
private Integer annee;
private Sexe sexe;
private Date dateNaissance;
private String adresse;

public Eleve(String nom, String prenom, Integer annee, Sexe sexe, Date dateNaissance, String adresse) {
...
}

// + getter-setter
...
}

3-B - Créer des DAO vides

Durée estimée : 1 minute.

Nous allons créer deux DAO vides : un pour charger la liste des élèves et l'autre pour charger les notes. Chaque DAO ne renvoie qu'une partie des informations disponibles. Nous verrons dans un prochain tutoriel comment réconcilier les données, ce qui n'est pas le sujet ici.

NoteDao.java

package com.thi.notes.dao;

import java.util.Map;

public class NoteDao {

public Map<String, Double> findNotesDernierExam() {
return null;
}
}

Ce DAO (Data Access Object) renvoie une « Map » dont les clés sont les noms (prénom + nom) des élèves et les valeurs sont les notes obtenues au dernier examen.

EleveDao.java

package com.thi.notes.dao;

import java.util.List;

import com.thi.notes.domain.Eleve;

public class EleveDao {

public List<Eleve> findEleves() {
return null;
}
}

Ce DAO renvoie la liste des élèves, tout simplement, en utilisant les objets Eleve et Sexe. Pour lire les données à l'aide de EleveDao, on utilisera un code ressemblant au suivant.

Utilisation de EleveDao

final EleveDao eleveDao = new EleveDao();
final List<Eleve> eleves = eleveDao.findEleves();

for(Eleve eleve : eleves) {
System.out.println(eleve);
}

Dans la suite de ce tutoriel, nous allons nous concentrer sur « EleveDao.java », l'autre DAO utilisant globalement les mêmes principes. Un code d'exemple est toutefois proposé en annexe et dans le fichier Zip final.

3-C - Ajout d'Open-CSV

Durée estimée : 30 secondes.

Pour utiliser Open CSV dans un projet Java-Maven, il suffit d'ajouter une dépendance dans le « pom.xml ».

Dépendance vers Open CSV

<dependency>
<groupId>net.sf.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>2.3</version>
</dependency>

Puis, comme d'habitude, il faut relancer une installation Maven.

Maven

mvn clean install eclipse:eclipse

En fonction des éléments déjà présents sur l'ordinateur, la réponse de Maven devrait ressembler à la trace suivante.

Maven

C:\dvp\notes>mvn clean install eclipse:eclipse

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Notes 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ notes ---
[INFO] Deleting C:\dvp\notes\target
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ notes ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO]
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ notes ---
[INFO] Compiling 9 source files to C:\dvp\notes\target\classes
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ notes ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 0 resource

...

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 10.197s
[INFO] Finished at: Wed Jan 18 20:19:12 CET 2012
[INFO] Final Memory: 12M/59M

3-D - Lecture des données

Durée estimée : 3 minutes.

On entre dans le vif du sujet. Comme indiqué plus haut, nous allons nous concentrer sur la lecture de la liste d'élèves.

Pour commencer, il faut accéder au fichier CSV.

Utilisation du helper

public class EleveDao {

private final static String RESOURCES_PATH = "src/main/resources/";
private final static String ELEVES_FILE_NAME = "eleves.csv";

public List<Eleve> findEleves() {

File file = new File(RESOURCES_PATH + ELEVES_FILE_NAME);
FileReader fr = new FileReader(file);

...
}
}

Puis j'utilise l'objet « CSVReader » fourni par la bibliothèque « Open-CSV ».

Utilisation du helper, sans la gestion des exceptions

public class EleveDao {

private final static char SEPARATOR = ';';
...

public List<Eleve> findEleves() {
File file = new File(RESOURCES_PATH + ELEVES_FILE_NAME);
FileReader fr = new FileReader(file);

CSVReader csvReader = new CSVReader(fr, SEPARATOR);
...

Ce « reader » nous permet de lire les données du fichier.

Lire les données

List<String[] > data = new ArrayList<String[] >();

String[] nextLine = null;
while ((nextLine = csvReader.readNext()) != null) {
int size = nextLine.length;

// ligne vide
if (size == 0) {
continue;
}
String debut = nextLine[0].trim();
if (debut.length() == 0 && size == 1) {
continue;
}

// ligne de commentaire
if (debut.startsWith("#")) {
continue;
}
data.add(nextLine);
}

Il ne reste plus qu'à transformer les données, qu'on a sous forme de tableau de String, en objets Eleve.

Transformer les données en objets

List<String[] > data = new ArrayList<String[] >();
...

List<Eleve> eleves = new ArrayList<Eleve>();

DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
for (String[] oneData : data) {
String nom = oneData[0];
String prenom = oneData[1];
String classeStr = oneData[2];
String sexeStr = oneData[3];
String dateNaissanceStr = oneData[4];
String adresse = oneData[5];

Integer classe = Integer.parseInt(classeStr);
Sexe sexe = (sexeStr.equalsIgnoreCase("F")) ? FEMME : HOMME;
Date dateNaissance = dateFormat.parse(dateNaissanceStr);

Eleve eleve = new Eleve(nom, prenom, classe, sexe, dateNaissance, adresse);
eleves.add(eleve);
}

return eleves;

Ici, la seule petite difficulté vient de la transformation de la date de naissance, d'un String vers un objet Date, ce que l'on fait à l'aide des classes Java « DateFormat » et « SimpleDateFormat ».

Transformer de la date de naissance

DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");

String dateNaissanceStr = "28/01/1992"; // Par exemple
Date dateNaissance = dateFormat.parse(dateNaissanceStr);

Le tutoriel « fichiers CSV en Java » propose une façon plus élégante de transformer les lignes du fichier en liste d'objets.

Il ne reste plus qu'à réorganiser un peu le code (pour éviter d'avoir des méthodes trop grosses) et à factoriser les traitements (et ainsi ne pas dupliquer de code dans l'autre DAO). Le code final, avec ces changements, est disponible en annexe.

4 - Conclusions

Comme on vient de le constater, il est relativement simple de lire un fichier CSV à l'aide d'une bibliothèque telle que Open-CSV. Cette dernière réalise un ensemble de traitements complexes et en masque la complexité. Il existe d'autres bibliothèques sur le marché, qui fonctionnent différemment et que je vous invite à regarder, au moins pas curiosité.

Et bien entendu, je vous encourage à lire mon tutoriel sur les fichiers CSV en Java.

Le code final de ce tutoriel est disponible dans le fichier ZIP notes4_domain-et-dao-csv.zip.

Retrouvez les tutoriels de la série « en 5 minutes » sur developpez.com à l'adresse https://thierry-leriche-dessirier.developpez.com/#page_articles.

5 - Remerciements

Je tiens à remercier, en tant qu'auteur de tutoriel rapide, toutes les personnes qui m'ont aidé et soutenu. Je pense tout d'abord à mes collègues qui subissent mes questions au quotidien, mais aussi à mes contacts et amis du Web, dans le domaine de l'informatique ou non, qui m'ont fait part de leurs remarques et critiques. Bien entendu, je n'oublie pas l'équipe de developpez.com qui m'a guidé dans la rédaction de cet article et m'a aidé à le corriger et le faire évoluer, principalement sur le forum.

Plus particulièrement j'adresse mes remerciements, à Claude LELOUP (ClaudeLELOUP), Mickael BARON (keulkeul), Nemek et Yan BONNEL.

6 - Annexes

6-A - Liens

Le site Web d'Open-CSV est disponible à l'adresse
http://opencsv.sourceforge.net

Pour suivre cet article, je conseille la lecture des tutoriels
« Importer un projet Maven dans Eclipse en 5 minutes » disponible à l'adresse
https://thierry-leriche-dessirier.developpez.com/tutoriels/java/importer-projet-maven-dans-eclipse-5-min

Je vous encourage à lire mon tutoriel sur les fichiers CSV en Java, disponible à l'adresse
https://thierry-leriche-dessirier.developpez.com/tutoriels/java/csv-avec-java

6-B - Les fichiers importants en entier

Le helper CsvFileHelper.java

package com.thi.notes.dao;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import au.com.bytecode.opencsv.CSVReader;

public class CsvFileHelper {

public static List<String[] > readCsvFile(String fileName, char separator) {
final List<String[] > data = new ArrayList<String[] >();

try {
final File file = new File(fileName);
final FileReader fr = new FileReader(file);

final CSVReader csvReader = new CSVReader(fr, separator);

String[] nextLine = null;
while ((nextLine = csvReader.readNext()) != null) {
final int size = nextLine.length;

// ligne vide
if (size == 0) {
continue;
}
final String debut = nextLine[0].trim();
if (debut.length() == 0 && size == 1) {
continue;
}

// ligne de commentaire
if (debut.startsWith("#")) {
continue;
}
data.add(nextLine);
}

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

return data;
}
}

Interface EleveDao.java

package com.thi.notes.dao;

import java.util.List;

import com.thi.notes.domain.Eleve;

public interface EleveDao {

List<Eleve> findEleves();
}

Implémentation CsvEleveDao.java

package com.thi.notes.dao;

import static com.thi.notes.dao.CsvFileHelper.readCsvFile;
import static com.thi.notes.domain.Sexe.FEMME;
import static com.thi.notes.domain.Sexe.HOMME;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.thi.notes.domain.Eleve;
import com.thi.notes.domain.Sexe;

public class CsvEleveDao implements EleveDao {

private final static char SEPARATOR = ';';
private final static String RESOURCES_PATH = "src/main/resources/";
private final static String ELEVES_FILE_NAME = "eleves.csv";

@Override
public List<Eleve> findEleves() {

final List<String[] > data = readCsvFile(RESOURCES_PATH + ELEVES_FILE_NAME, SEPARATOR);

final List<Eleve> eleves = dataToEleves(data);

return eleves;
}

private List<Eleve> dataToEleves(List<String[] > data) {
final List<Eleve> eleves = new ArrayList<Eleve>();

try {
final DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
for (String[] oneData : data) {
final String nom = oneData[0];
final String prenom = oneData[1];
final String classeStr = oneData[2];
final String sexeStr = oneData[3];
final String dateNaissanceStr = oneData[4];
final String adresse = oneData[5];

final Integer classe = Integer.parseInt(classeStr);
final Sexe sexe = (sexeStr.equalsIgnoreCase("F")) ? FEMME : HOMME;
final Date dateNaissance = dateFormat.parse(dateNaissanceStr);

final Eleve eleve = new Eleve(nom, prenom, classe, sexe, dateNaissance, adresse);
eleves.add(eleve);
}
} catch (ParseException e) {
e.printStackTrace();
}

return eleves;
}
}

Dans un prochain tutoriel, je vous montrerai comment charger la liste d'élèves à partir d'une base de données MySql, et non plus à partir d'un fichier CSV. Néanmoins, la signature de la méthode findEleves ne devrait pas changer. C'est pourquoi j'ai ajouté une interface. Comme vous pouvez le deviner, de la même manière que CsvEleveDao implémente EleveDao, le prochain tutoriel introduira l'implémentation DatabaseEleveDao de EleveDao pour lire les données depuis la base.

Interface NoteDao.java

package com.thi.notes.dao;

import java.util.Map;

public interface NoteDao {

Map<String, Double> findNotesDernierExam();
}

Implémentation CsvNoteDao.java

package com.thi.notes.dao;

import static com.thi.notes.dao.CsvFileHelper.readCsvFile;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CsvNoteDao implements NoteDao {

private final static char SEPARATOR = ';';
private final static String RESOURCES_PATH = "src/main/resources/";
private final static String ELEVES_FILE_NAME = "notes-dernier-exam.csv";

@Override
public Map<String, Double> findNotesDernierExam() {

final List<String[] > data = readCsvFile(RESOURCES_PATH + ELEVES_FILE_NAME, SEPARATOR);

final Map<String, Double> map = dataToMap(data);

return map;
}

private Map<String, Double> dataToMap(List<String[] > data) {
final Map<String, Double> map = new HashMap<String, Double>();

for (String[] oneData : data) {

final String nom = oneData[0];
final String prenom = oneData[1];

final String noteStr = oneData[2];
final Double note = new Double(noteStr.replace(",", "."));

map.put(nom + " " + prenom, note);
}

return map;
}
}

DaoLauncher.java pour vérifier que ça marche

package com.thi.notes;

import java.util.List;
import java.util.Map;

import com.thi.notes.dao.CsvEleveDao;
import com.thi.notes.dao.CsvNoteDao;
import com.thi.notes.dao.EleveDao;
import com.thi.notes.dao.NoteDao;
import com.thi.notes.domain.Eleve;

public class DaoLauncher {

public static void main(String[] args) {

final EleveDao eleveDao = new CsvEleveDao();
final List<Eleve> eleves = eleveDao.findEleves();

System.out.println("Liste des eleves");
for (Eleve eleve : eleves) {
System.out.println(eleve);
}

final NoteDao noteDao = new CsvNoteDao();
final Map<String, Double> notes = noteDao.findNotesDernierExam();
System.out.println("Notes des eleves");
for (String key : notes.keySet()) {
final Double note = notes.get(key);
System.out.println(key + " : " + note);
}
}
}

6-C - Utilisation de la lib MoteurCsv

Comme me l'a proposé Yan Bonnel, j'ai adapté le code présenté ci-dessus pour employer la bibliothèque MoteurCsv, qui ressemble un peu à JPA dans son utilisation.

Tout d'abord, je change mon pom.xml, sachant que MoteurCsv possède déjà une dépendance vers OpenCSV.

Extrait du fichier pom.xml utilisant MoteurCsv

<!-- MoteurCSV -->
<dependency>
<groupId>fr.ybo</groupId>
<artifactId>moteurcsv</artifactId>
<version>1.1.3</version>
</dependency>

pom.xml utilisant MoteurCsv

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.thi</groupId>
<artifactId>notes-avec-moteurcsv</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

...

<developers>
<!-- Thierry -->
<developer><name>Thierry Leriche-Dessirier</name></developer>

<!-- Yan -->
<developer><name>Yan Bonnel</name></developer>
</developers>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>4.8.2</junit.version>
<jfreechart.version>1.0.14</jfreechart.version>

<!-- Remplacement de opencsv par moteurcsv -->
<!--<opencsv.version>2.3</opencsv.version>-->
<moteurcsv.version>1.1.3</moteurcsv.version>
</properties>

<dependencyManagement>
<dependencies>
...

<!-- Open CSV -->
<!--<dependency>
<groupId>net.sf.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>${opencsv.version}</version>
</dependency>-->

<!-- MoteurCSV -->
<dependency>
<groupId>fr.ybo</groupId>
<artifactId>moteurcsv</artifactId>
<version>${moteurcsv.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
...

<!-- Open CSV -->
<!--<dependency>
<groupId>net.sf.opencsv</groupId>
<artifactId>opencsv</artifactId>
</dependency>-->

<!-- MoteurCSV -->
<dependency>
<groupId>fr.ybo</groupId>
<artifactId>moteurcsv</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>

<repositories>
<repository>
<id>ybonnel-release</id>
<url>https://repository-ybonnel.forge.cloudbees.com/release/</url>
</repository>
</repositories>

</project>

Je vais ensuite modifier mes beans pour que ça ressemble à du JPA.

Eleve.java

@FichierCsv(separateur = ";")
public class Eleve {

// private String nom;
// private String prenom;
// private Integer annee;
// private Sexe sexe;
// private Date dateNaissance;
// private String adresse;

@BaliseCsv("Nom")
private String nom;
@BaliseCsv("Prénom")
private String prenom;
@BaliseCsv(value = "Classe", adapter = AdapterInteger.class)
private Integer annee;
@BaliseCsv(value = "Sexe", adapter = AdapterSexe.class)
private Sexe sexe;
@BaliseCsv(value = "Date de naissance", adapter = AdapterDate.class)
private Date dateNaissance;
@BaliseCsv(value = "Adresse")
private String adresse;

public Eleve() {
// Constructeur par defaut obligatoire pour MoteurCsv.
}

...

Sexe.java

public enum Sexe {

HOMME("Garçon"),
FEMME("Fille");

private final String label;

Sexe(String label) {
this.label = label;
}

public String getLabel() {
return label;
}

public static class AdapterSexe implements AdapterCsv<Sexe> {

@Override
public Sexe parse(String chaine) {
return (chaine.equalsIgnoreCase("F")) ? FEMME : HOMME;
}

@Override
public String toString(Sexe objet) {
return objet == FEMME ? "F" : "H";
}
}

}

Je peux maintenant passer à la lecture du fichier.

CsvEleveDao.java

public class CsvEleveDao implements EleveDao {

private final static char SEPARATOR = ';';
private final static String RESOURCES_PATH = "src/main/resources/";
private final static String ELEVES_FILE_NAME = "eleves.csv";

// Moteur CSV
private final static MoteurCsv moteurCsv = new MoteurCsv(Eleve.class);

@Override
public List<Eleve> findEleves() {

// final List<String[] > data = readCsvFile(RESOURCES_PATH +
// ELEVES_FILE_NAME, SEPARATOR);
// final List<Eleve> eleves = dataToEleves(data);

List<Eleve> eleves = null;
try {
eleves = moteurCsv.parseInputStream(new FileInputStream(new File(RESOURCES_PATH + ELEVES_FILE_NAME)), Eleve.class);
} catch (FileNotFoundException e) {
e.printStackTrace();
eleves = new ArrayList<Eleve>();
}

return eleves;
}

public static class AdapterDate implements AdapterCsv<Date> {

final DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");

@Override
public Date parse(String chaine) {
try {
return dateFormat.parse(chaine);
} catch (ParseException e) {
return null;
}
}

@Override
public String toString(Date objet) {
return dateFormat.format(objet);
}

}

Ici, je ne m'occupe que de la liste des élèves. Pour lire l'autre fichier, il faut faire la même chose.

Petit détail qui compte, MoteurCsv a besoin, par défaut, que la première ligne importante contienne les noms des champs, puisqu'ils sont utilisés pour « mapper » les objets.

eleves.csv

Nom;Prénom;Classe;Sexe;Date de naissance;Adresse

Durand;Marie;3;F;02/01/1992;15 rue du Lac 75001 Paris
Alesi;Julie;3;F;08/01/1992;72 av. Jean Dupont 75003 Paris
Martini;Carine;3;F;17/01/1992;2 rue du Moulin 92230 Neullavy
...

Et voilà… C'est encore plus simple.

Le code final de ce tutoriel, incluant l'utilisation de MoteurCsv est disponible dans le fichier ZIP notes4_moteurcsv.zip.

6-C-1 - Version 1.2.0 de la lib

Mise à jour : 12 juillet 2012.

Les nouvelles versions de la lib MoteurCsv (par exemple la version 1.2.0) s'utilisent d'une autre manière que ce qui est présenté ci-dessus. La méthode « parseInputStream() » renvoie désormais un objet de type « Resultat » et non plus la liste des objets lus.

Objet Resultat

public class CsvEleveDao implements EleveDao {

private final static char SEPARATOR = ';';
private final static String RESOURCES_PATH = "src/main/resources/";
private final static String ELEVES_FILE_NAME = "eleves.csv";

// Moteur CSV
private final static MoteurCsv moteurCsv = new MoteurCsv(Eleve.class);

@Override
public List<Eleve> findEleves() {

Resultat<Eleve> resultat =  moteurCsv.parseInputStream(new FileInputStream(new File(RESOURCES_PATH + ELEVES_FILE_NAME)), Eleve.class);
List<Eleve> eleves = resultat.getObjets();

Cet objet « Resultat » propose la méthode « getErreurs() » qui montre les éventuelles erreurs qui sont arrivées durant les traitements.

En plus de ce changement, la lib propose une nouvelle syntaxe (prog fonc) qui fera un peu penser à celle utilisée dans Guava. Elle permet de traiter les éléments trouvés directement.

parseFileAndInsert

public class CsvEleveDao implements EleveDao {

final MoteurCsv moteurCsv = new MoteurCsv(Eleve.class);
...

@Override
public List<Eleve> findEleves() {

final File f = new File(RESOURCES_PATH + ELEVES_FILE_NAME);
final FileReader fr = new FileReader(f);

final List<Eleve> eleves = newArrayList(); // Static factory Guava
moteurCsv.parseFileAndInsert(fr, Eleve.class, new InsertObject<Eleve>() {

@Override
public void insertObject(Eleve eleve) {
foo(eleve); // Faire un truc sur l'eleve.
...
result.add(eleve); // Mettre dans la liste, si besoin.
...
System.out.println(eleve); // Un petit sysout pour le plaisir.
}
});