1.6- Flux et fichiers


0 Généralités sur les fichiers

1 Classe File et parcours du système de fichiers

2 Manipulation des fichier textes : écriture et lecture

3 Flux d'objets-serialisation

4 CSV

5 XML


0 Généralités sur les fichiers et les flux

La notion de flux est très générale. Dans System.out.println, l'attribut out de la classe System est un flux de sortie. La notion de flux de sortie est très générale en java ou en C++ : elle renvoie à n'importe quel "canal" suceptible de recevoir de l'information sous forme d'une suite d'octets. Il peut s'agir d'un périphérique d'affichage comme avec l'objet out, mais il peut aussi s'agir d'un fichier ou d'une connexion à un site. Deux grands types de flux : les flux binaires et les flux textes. Dans le second cas, l'information du flux est modifiée pour que la transmission soit celle d'une suite de caractères (on parle de formatage).

Cette distinction se retrouve entre les différents flux sur fichiers. Il existe deux types de fichiers : les fichiers binaires et les fichiers text. Pour ces derniers, on peut les ouvrir avec un éditeur de texte et avoir un résultat "interprétable" (que le fichier soit enregistré au format .txt, .xml, .java etc...) tandis que l'ouverture d'un fichier binaire dans un éditeur de texte donne un résultat illisible (essayer d'ouvrir un .exe dans un éditeur de texte par exemple).

On ne propose pas d'aller plus avant dans le développement des disinctions : le cours se place à un niveau pratique. En l'occurence, Java propose des objets d'assez haut niveau pour que beaucoup des difficultés restent transparentes.

L'identification des fichiers se fait par un chemin absolu. Ces chemins se manipulent comme les String. Noter que pour les chaînes, la manipulation du caractère \ se fait par '\\' au sein des chaînes. Donc pour désigner le fichier c:\Documents\Recents\MonTexte.txt, on récupère le chemin : String chemin="c:\\Documents\\Recents\\MonTexte.txt";. Attention, le séparateur de noms de repertoires change en fonction de votre système d'exploitation : \ sous Windows, mais / sous Unix ou Linux. Pour créer un code ne dépendant pas d'une plateforme ou de l'autre : utiliser File.separator qui sera initialisé en fonction du système sur lequel le programme tourne. Par exemple, plutôt que : String chemin="c:\\Documents\\Recents\\MonTexte.txt";, on peut mettre
String chemin="c:"+File.separator+"Documents"+File.separator+"Recents"+File.separator+"MonTexte.txt";.


1 Classe File et parcours du système de fichiers

La classe File permet d'instancier des objets qui réferencent aussi bien des répertoires que des fichiers. On l'utilise pour le parcours et la manipulation du système de fichiers. On crée un objet avec le passage d'une chaîne en paramètre, cette chaîne désigne un repertoire ou un fichier :

String chemin="c:\\etud\\";
File fichier=new File(chemin)


A partir d'un objet File traité sans a priori, il peut réferencer un repertoire ou un fichier. Pour le savoir, on utilise isDirectory() :

public void fonctionManipulationFile(File fichier)
	{
		if(fichier.isDirectory())
		...
	}


Si l'objet File est un repertoire, alors il est possible de recupérer la liste des fichiers sous forme de tableau File[] avec : listFiles();. Quelques unes des fonctions utiles :


2 Manipulation des fichier textes : écriture et lecture

Ecriture de fichier

Pour l'écriture de fichier texte, l'ouverture d'un flux sur un fichier texte se fait par l'instanciation d'un objet FileOutputStream avec une String qui correspond à un chemin en entrée. Si le fichier existe déjà, l'existant sera écrasé, sinon, le fichier sera crée.

La manipulation effective des flux induit d'introduire une nouvelle notion : la notion d'Exception. Cette notion existe dans plusieurs languages. Une Exception est une erreur déclenchée, pour laquelle il est possible de rendre le code réactif : on traite l'erreur sans que le code s'interrompe. En java, un certain nombre de fonctions sont définies comme pouvant déclencher une exception. Si je veux indiquer qu'une fonction que je déclare peut déclencher une Exception : public void maFonction(int a,int b) throws Exception. Dans ce cas, au sein du code de maFonction, il y a un point de création de l'Exception. Lors de l'exécution de la fonction : soit l'exécution se déoule sans déclencher l'instruction d'Exception, soit l'instruction de déclenchement de l'Exception est exécutée : l'exécution de la fonction s'interrompt alors et l'Exception est passé au code appelant la fonction qui doit prévoir de la traiter. Ainsi, le code qui contient la fonction est organisé :
public void appelMaFonction()
	{
		try
		{
		instruction à exécuter
		maFonction(4,5);
		instruction à exéctuer
		}catch(Exception){Les instructions exécutées en cas d'Exception déclenchée lors de l'exécution de maFonction}
	}
Exception est une interface pour laquelle il est possible de créer des sous-classes, par exemple, le fait d'introduire une division par 0 déclenche une ArithmeticException. Si l'Exception n'est pas traitée (récupérée par try{...}catch(){...}) : le code s'arretera. le thème sera traité de manière plus importante dans un cours suivant.

Pour écrire un fichier, il existe différentes manières de faire les choses, je vous en propose une. Elle induit l'utilisation des Exception, mais toutes les approches de la création de fichier text l'induirait : ceci vient du fait qu'il est toujours possible qu'il y ait un problème d'écriture ou de lecture sur le disque avec le chemin utilisé. On se donne une fonction intermédiaire (dans une classe à part ou dans une classe du programme courant) :

public static void ecrireFichier(FileOutputStream stream,String s)	
	{
		try{
		stream.write(s.getBytes());
		}catch(Exception e){System.out.println("Exception in ecrireFichier(FileOutputStream)");}
	}
Il suffit ensuite d'ouvrire un flux FileOutputStream puis on écrit des Sting dans ce fichier en appellant des fonctions ecrireFichier(fos,chaine); Par exemple (à exécuter puis observer le fichier c:\Mon Document.txt" :
import java.io;

public class TestEcriture{

public static void main(String[] args)
	{
	FileOutputStream fos=null;
		try
		{
		fos=new FileOutputStream("c:\\Mon document.txt");
		}catch(Exception e){System.out.println("Declenchement d'Exception : fonction main de TestEcriture");}
	TestEcriture.ecrireFichier(fos,"Un premier exemple d'ecriture de fichier");
	TestEcriture.ecrireFichier(fos,"\n");
	/*Fermeture du flux nécessaire : cf les exercices*/
		try
		{
		fos.close();
		}catch(Exception e){System.out.println("Declenchement d'Exception : fonction main de TestEcriture");}
	}

public static void ecrireFichier(FileOutputStream stream,String s)	
	{
		try{
		stream.write(s.getBytes());
		}catch(Exception e){System.out.println("Exception in ecrireFichier(FileOutputStream)");}
	}

}


Lecture de fichier

En ayant un fichier text, il est également possible de le lire. On ouvre pour cela un flux : FileInputStream. On peut créer un FileOutputStream à partir d'un objet File ou à partir d'une String qui correspond au chemin : Ensuite, deux fonctions permettent de manipuler symplement le fichier : Un exemple de manipulation se dégage très rapidement :

public static void main(String[] args)
	{
	byte[] tab=null;
		try
		{
		/*Ouverture du flux*/
		FileInputStream fis=new FileInputStream("c:\\MonDoc.txt");
		/*Creation du tableau de la taille le nombre de caractère du fichier*/
		tab=new byte[fis.available()];
		/*Le flux des octets du fichiers est mis dans le tableau*/
		fis.read(tab);
		}catch(Exception e){System.out.println("Exception dans main");}
	
		if(tab!=null && tab.length>=5)
		{
		System.out.println("Les 5 premiers caracteres du fichier : ");
			for(int i=0;i<5;i++)
			{
			/*Obtention d'un caractère à partir de l'octet*/
			char c=(char)tab[i];
			/*Affichage*/
			System.out.print(i+" "+c+"\t");
			}
		}
	}


Des chaines de caractres aux autres types de variables : parser

Pour passer depuis les chaînes à des valeurs entières, réelles ou à des dates en fonction, ie par exemple pour passer d'une variable String "4" à une variable int 4, on utilise des fonctions d'interprétation.

Pour obtenir une valeur entière à partir d'une chaîne String st : int parseInt(String) qui est une fonction static de la classe Integer. Renvoie une numberFormatException si la chaîne n'est pas interprétable comme un Integer. De même, la fonction double parseDouble(String); de la classe Double renvoie un double si la chaîne est interprétable.

Exemple :
/*Affecte 5 à t*/
int t=Integer.parseInt("5");

/*Autres exemple d'affectation :*/
String str="65.7898";
String str2="100E-6";
double x1=Double.parseDouble(str);
double x2=Double.parseDouble(str2);
System.out.println((x1+x2))/*Cette derniere instruction affiche 65.7899*/


On donne également un exemple de code pour parser les formats date sans insister dessus, il faudrait au préalable un cours sur la manipulation des dates :

String chaineALire="1997-04-21";
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
Date dt = fmt.parse(chaineALire);
GregorianCalendar gC=(GregorianCalendar)fmt.getCalendar();
gC.setTime(dt);	


3 Flux d'objets-serialisation

La sérialisation consiste en l'enregistrement d'objet dans des fichiers binaires. Un objet doit implémenter l'interface java.io.Serializable pour pouvoir être placé dans un fichier. Utilisation de ObjectOutputStream ensuite pour avoir manipulation de l'objet : création d'un flux de sortie d'objet. Exemple d'enregistrement d'objet dans un fichier :

	try
	{
	FileOutputStream out=new FileOutputStream("File");
	ObjectOutputStream s=new ObjectOutputStream(out);
	String str="Today";
	s.writeObject(str);
	s.writeObject(new Date());
	s.flush();
	s.close();
	}catch(Exception e){}
Le code précédent entraîne la création d'un fichier File sur le disque dur (on pourrait spécifier un chemin absolu).

Exemple de récupération des objets sérialisés
	try
	{
	FileInputStream in=new FileInputStream("File");
	ObjectInputStream s=new ObjectInputStream(in);
	/*Le programmeur peut transtyper puisque qu'il sait l'ordre des types des objets enregistrés*/
	String str=(String)s.readObject();
	Date dt=(Date)s.readObject();
	}catch(Exception e){}


4 CSV

Pour la création de fichiers CSV : on crée un fichier texte et on organise le fichier en séparant les données avec un séparateur (la virgue) et en les organisant en ligne. On suppose avoir une liste listePersonnes d'objets de la classe Personne et une fonction d'écriture sur un FileOutputStream (voir 8.2) : ecrireFichierTxt(FileOutputStream,String). On peut alors créer un fichier CSV :

public static void createCSV(FileOutputStream fos,LinkedList listePersonnes)
	{
		for(int i=0;i<listePersonnes.size();i++)
		{
		Personne per=(Personne)listePersonnes.get(i);
		ecrireFichier(fos,per.getName()+","+per.getSurname()+","+getAge()+","+getPatrimoine()+"\n");
		}
	}


Pour la récuperation des données CSV à partir d'un fichier file.csv. On utilise la classe
CSVReader.java (qui n'est pas dans l'API java mais a été développée et mise à disposition par la communauté java). La fonction suivante permet la récuperation des données et la création des objets Personne :

public static void cours(String fichier,MyDate dateRef)
	{
	CSVReader csvR;
	Double coursDateRef=null;	
	String[] tab;	
		try
		{
		csvR=new CSVReader(new FileReader(fichier),',');
		tab=csvR.readNext();
		}catch(Exception e){System.out.println("Probleme pour lecture Reader");}			
	}


5 XML

On n'évoque pas ici la manipulation des fichiers XML. Elle se fait avec le package javax.swing.xml.parsers;