Aller au menu - Aller au contenu

Icône Les flux d'entrées / sorties (2/2)

Mise à jour : 12/02/2010
Difficulté : Intermédiaire Intermédiaire Creative Commons BY-NC-SA
96 702 visites depuis 7 jours, dont 438 sur ce chapitre classé 4/786
Nous voilà dans la dernière ligne droite concernant le flux d'entrées / sorties.

Dans ce chapitre, nous allons aborder une autre hiérarchie de classes présente dans le package java.io .
Les super-classes de la deuxième hiérarchie sont :
  • la classe Reader ;
  • la classe Writer.


Vous verrez que l'utilisation des classes de cette hiérarchie est très semblable à ce que nous avons vu lors du chapitre précédent, à une différence près : ces classes ne vont pas lire / écrire des données binaires, mais des caractères UNICODE ! :waw:

Je ne vais pas vous gâcher la surprise... Alors, on y va ?
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Les objets CharArray(Writer/Reader) et String(Writer/Reader)

Nous allons utiliser des objets :
  • CharArray(Writer/Reader) ;
  • String(Writer/Reader).

Ces deux types jouent quasiment le même rôle et ont les mêmes méthodes : celles de leur classe mère !
Ces deux objets n'ajoutent donc aucune nouvelle fonctionnalité à leur objet mère.

Leur principale fonction est de permettre d'écrire un flux de caractères dans un tampon de mémoire adaptatif : un emplacement en mémoire qui peut changer de taille selon les besoins.

Nous n'en avons pas parlé dans le chapitre précédent afin de ne pas l'alourdir, mais il existe des classes remplissant le même rôle que ces classes-ci : ByteArray(Input/Output)Stream.


Voyons comment utiliser ces deux objets.

Nous allons commencer par un exemple commenté des objets CharArray(Writer/Reader), retour en mode console :

Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//Package à importer afin d'utiliser l'objet File
import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.IOException;

public class Main {
	public static void main(String[] args) {
		CharArrayWriter caw = new CharArrayWriter();
		CharArrayReader car;
		
		try {
			caw.write("Coucouc les zéros");
			//Appel à la méthode toString 
			//de manière tacite, de notre objet
			System.out.println(caw);
			
			//caw.close() n'a aucun effet sur le flux
			//Seul caw.reset() peut tout effacer
			caw.close();
			
			//on passe un tableau de caractères à l'objet
			//qui va lire le tampon
			car = new CharArrayReader(caw.toCharArray());
			
			int i ;
			//On remet tous les caractères lus dans un String
			String str = "";
			while(( i = car.read()) != -1)
				str += (char) i;
			
			System.out.println(str);
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}


Je vous laisse le soin d'examiner ce code ainsi que ses effets. Celui-ci est assez commenté, il me semble, pour que vous en compreniez toutes les subtilités. :p

L'objet String(Writer/Reader) fonctionne de la même façon :

Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//Package à importer afin d'utiliser l'objet File
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;

public class Main {
	public static void main(String[] args) {
		StringWriter sw = new StringWriter();
		StringReader sr;
		
		try {
			sw.write("Coucouc les zéros");
			//Appel à la méthode toString 
			//de manière tacite, de notre objet
			System.out.println(sw);
			
			//caw.close() n'a aucun effet sur le flux
			//Seul caw.reset() peut tout effacer
			sw.close();
			
			//on passe un tableau de caractères à l'objet
			//qui va lire le tampon
			sr = new StringReader(sw.toString());
			
			int i ;
			//On remet tous les caractères lus dans un String
			String str = "";
			while(( i = sr.read()) != -1)
				str += (char) i;
			
			System.out.println(str);
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}


En fait, il s'agit du même code avec des objets différents ! ;)
Vous savez maintenant comment écrire un flux texte dans un tampon de mémoire...
Peut-être en aurez-vous besoin un jour, qui sait ?

Je vous propose maintenant de voir comment traiter les fichiers texte avec des flux de caractères.

les classes File(Writer/Reader) et Print(Writer/Reader)

Comme nous l'avons vu dans le chapitre précédent, les objets travaillant avec des flux utilisent des flux binaires.
La conséquence est que, même si vous ne mettez que des caractères dans un fichier, que vous le sauvegardez, les objets vus précédemment traiteront votre fichier comme un fichier contenant des données binaires !

Nous allons voir que, dans le package java.io , les objets citées dans le titre de cette sous-section servent à lire / écrire des données d'un fichier texte.

Ce que nous allons faire, c'est tout simplement créer un nouveau fichier et le lire, et le tout en Java !
Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//Package à importer afin d'utiliser l'objet File
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Main {
	public static void main(String[] args) {
		
		File file = new File("testFileWriter.txt");
		
		FileWriter fw;
		FileReader fr;
		
		
		try {
			//Création de l'objet
			fw = new FileWriter(file);
			String str = "Bonjour à tous amis ZérOs !\n";
			str += "\tComment allez-vous ? \n";
			//On écrit la chaîne
			fw.write(str);
			//On ferme le flux
			fw.close();
			
			//création de l'objet de lecture
			fr = new FileReader(file);
			str = "";
			int i = 0;
			//Lecture des données
			while((i = fr.read()) != -1)
				str += (char)i;
			
			//affichage
			System.out.println(str);
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}


Vous pouvez voir que l'affichage est bon et qu'un nouveau fichier vient de faire son apparition dans le dossier contenant votre projet Eclipse !
Tout comme dans le chapitre précédent, la lecture d'un fichier inexistant entraîne une FileNotFoundException , et l'écriture peut entraîner un IOException !


Classes très simples à utiliser maintenant que vous savez utiliser les flux binaires...
En fait ce chapitre est un peu un clone du précédent, mais les objets ne travaillent pas avec le même type de données. :p

Pour voir la liste des classes présentes dans cette hiérarchie, allez faire un tour dans l'annexe prévue à cet effet.

Cependant, depuis le JDK 1.4, un nouveau package a vu le jour visant à améliorer les performances des flux, buffers... Traités par java.io . Car, ce que vous ignorez encore, c'est que le package que nous explorons depuis le chapitre précédent existe depuis la version 1.1 du JDK.

Il était temps d'avoir une remise à niveau afin d'améliorer les résultats obtenus avec les objets traitant les flux. C'est là que le package java.nio a vu le jour !

Du renouveau chez les flux : le package java.nio

Vous l'avez sûrement deviné, mais "nio" signifie : New I/O.
Comme je vous l'ai dit précédemment, ce package a été créé afin d'améliorer les performances sur le traitement des fichiers, du réseau et des buffers.
Nous parlerons de la programmation réseau dans un chapitre dédié à ce type de programmation.


Ce package offre une nouvelle façon de lire les données. Nous nous intéresserons uniquement à l'aspect fichier, pour le moment.
Vous avez pu constater que les objets du package java.io traitaient les données par octets. Les objets du package java.nio , eux, les traitent par blocs de données : ce qui signifie que la lecture en est accélérée !

Tout repose sur deux objets dans ce nouveau package : les channels et les buffers .
Les channels sont en fait des flux, tout comme dans l'ancien package, mais ceux-ci sont amenés à travailler avec un buffer dont vous définissez la taille !

Pour simplifier au maximum


Lorsque vous ouvrez un flux vers un fichier avec un objet FileInputStream, vous pouvez récupérer un canal vers ce fichier. Celui-ci, combiné avec un buffer, vous permettra de lire votre fichier encore plus vite qu'avec un BufferedInputStream ! :waw:

Reprenez le gros fichier que je vous ai fait faire dans le chapitre précédent. Voici l'adresse à laquelle le retrouver pour ceux qui auraient déjà effacé le dit fichier.

Nous allons maintenant le relire avec ce nouveau package en comparant le buffer conventionnel et la nouvelle façon de faire :

Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//Package à importer afin d'utiliser l'objet File
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;

public class Main {
	public static void main(String[] args) {
		
		FileInputStream fis;
		BufferedInputStream bis;
		FileChannel fc;
		
		try {
		
			//Création des objets
			fis = new FileInputStream(new File("test.txt"));
			bis = new BufferedInputStream(fis);
			//Démarrage du chrono
			long time = System.currentTimeMillis();
			//Lecture
			while(bis.read() != -1);
			//Temps d'exécution
			System.out.println("Temps d'exécution avec un buffer conventionnel : " + (System.currentTimeMillis() - time));
			
			//Re-création d'un flux de fichier
			fis = new FileInputStream(new File("test.txt"));
			//On récupère le canal
			fc = fis.getChannel();
			//On en déduit la taille
			int size = (int)fc.size();
			//On crée un buffer 
			//correspondant à la taille du fichier
			ByteBuffer bBuff = ByteBuffer.allocate(size);
			
			//Démarrage du chrono
			time = System.currentTimeMillis();
			//Démarrage de la lecture
			fc.read(bBuff);
			//On prépare à la lecture avec l'appel à flip
			bBuff.flip();
			//Affichage du temps d'exécution
			System.out.println("Temps d'exécution avec un nouveau buffer : " + (System.currentTimeMillis() - time));
			
			//Vu que nous avons pris un buffer de byte
			//Afin de récupérer les données, nous pouvons utiliser 
			//un tableau de byte
			//La méthode array retourne un tableau de byte
			byte[] tabByte = bBuff.array();
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}


Et le résultat :

Image utilisateur


Vous constatez que les gains de performance ne sont pas négligeables...
Il est vrai aussi que ce nouveau package est le plus souvent utilisé pour les flux circulant sur les réseaux...
Je ne m'attarderai donc pas sur le sujet, mais une petite présentation était de mise. ;)

Vous devez savoir tout de même que ce package offre un buffer par type primitif pour la lecture sur le channel, vous trouverez donc les classes :
  • IntBuffer ;
  • CharBuffer ;
  • ShortBuffer ;
  • ByteBuffer ;
  • DoubleBuffer ;
  • FloatBuffer ;
  • LongBuffer.



Ce chapitre avait pour vocation de vous présenter le reste des classes disponibles dans la hiérarchie du package java.io et de vous présenter le package java.nio .

Vu qu'il n'y a rien de compliqué et de franchement nouveau (à part nio), je vous fais grâce du topo et du QCM... Mais ce sera l'une des rares fois ! ^^
Je vous conseille de prendre le temps de bien digérer tout ça, de faire des tests, de fumer une clope, de boire un café...
Bref, ne vous jetez pas tout de suite sur le prochain chapitre. ^^
La gestion des flux n'est pas quelque chose d'évident.

Après la pause syndicale, je vous propose un TP des familles, histoire de mettre en pratique tout ce vous avez vu... Les flux y compris. :diable:

En avant pour : le jeu du pendu !
Chapitre précédent Sommaire Chapitre suivant

Partager

8 commentaires pour "Les flux d'entrées / sorties (2/2)"
Note moyenne : 3.57 / 4 (1025 votes)
Pseudo Commentaire
Hors ligne kayzra # Posté le 07/02/2010 à 11:28:53
Avatar

Apriori NIO n'est valable que pour de gros fichier en tout cas niveau de la vitesse !
Hors ligne theo1999 # Posté le 02/06/2011 à 10:01:38

Je trouve que la boucle While:
Code : Java
1
2
3
while((i = fr.read()) != -1)
str += (char)i;
System.out.println(str);

est bizarre :euh: .Pourquoi elle nas pas de "{" et de "}" ou de ";" o_O ?
Est-ce de l'inattention dans le chapitre la concernant(mienne :-° ou tienne)?
Hors ligne Blocks # Posté le 31/07/2011 à 03:47:19
Avatar

theo1999 la boucle est normale, le while n'est que sur la ligne 2.

A part ça, c'est pas joli joli d'inciter les lecteurs à fumer o_O :
"Je vous conseille de prendre le temps de bien digérer tout ça, de faire des tests, de fumer une clope, de boire un café..."

Autoproclamé ~Chevalier des Edits.
 
Hors ligne curieuse_prog # Posté le 30/08/2011 à 18:14:20

Bonjour et merci pour ce très bon tuto.

J'ai quelques questions à propos des trois exemples commentés des chapitre sur les objets CharArray(Writer/Reader)et String((Writer/Reader))

exemple commenté objet CharArray(Writer/Reader)
1-concernant la méthode write de la classe CharArrayWriter:

permet-elle d'écrire directement des caractères unicode dans l'instance caw ou permet -elle d'y écrire des valeurs binaires.

Quand on affiche la valeur de caw après l'appel de la méthode write( System.out.println(caw);)on obtient des caractères unicode, ce qui laisse à penser que ce sont des caractères unicode qui sont directement écrits dans l'instance caw.

Mais peut être que je me trompe.

Cependant, lorsqu'on appelle la méthode read de la classe CharArrayReader, pour l'instance car( i=car.read()!=-1), quand on récupère la valeur de i sans faire de conversion vers le type char, on obtient une valeur binaire.

Est-ce que dans le tableau obtenu par l'appel de la méthode toCharArray( caw.toCharArray()), il existe des caractères unicode .
Si c'est le cas, est-ce l'appel de la méthode read de la classe CharArrayReader qui provoque une conversion des caractères unicode en valeurs binaires.

Je vous remercie beaucoup de bien vouloir m'éclairer sur ce sujet.

2-au niveau du commentaire de cette ligne de code

caw.write("coucou les zeros"); il est ecrit:
Citation
appel à la méthode toString de manière tacite, de notre objet.


Ne s'agit il pas plutôt de la méthode write, car je ne vois pas d'appel à la méthode toString.

3-Dernière remarque : à quoi sert l'appel à la méthode close pour l'instance caw( caw.close(), puisqu'il est écrit qu'elle n'a aucun effet sur le flux



exemple commenté de l'objet String(Writer/Reader)
1-Même remarque que précédemment:
au niveau du commentaire de cette ligne de code
sw.write("coucou les zeros");, il est écrit
Citation
//Appel à la méthode toString de manière tacite, de notre objet.

Ne s'agit-il pas de l'appel à la méthode write?


2-a propos de cette ligne de code
sw.close();
il est ecrit dans le commentaire: caw.close() n'a aucun effet sur le flux, seul caw.reset() peut tout effacer.
Ne faut-il pas remplacer caw par sw dans cette ligne de commentaire?
Sinon, même question que plus haut: si sw.close() n'a aucun effet sur le flux, pourquoi appeler cette méthode alors?

3-a propos de cette ligne de code
sr = new StringReader(sw.toString()); , il est ecrit au niveau du commentaire :
Citation
on passe un tableau de caractères à l'objet qui va lire le tampon.


l'appel à la méthode toString permet-elle de créer un tableau de caractères?
Je te remercie beaucoup de ta réponse

4-même question que plus haut: la méthode read de la classe Stringreader lit-elle des valeurs binaires ou lit elle des caractères unicode et les convertit chacun en valeur binaire.

Je te remercie encore beaucoup pour le temps que tu prendras à bien vouloir répondre à toutes ces questions.
Cela m'aidera beaucoup à mieux "digérer" cette partie liée aux flux.

Bien cordialement.

curieuse_prog
Hors ligne JoTwo # Posté le 30/11/2011 à 20:28:09
À prononcer DJOTOU
Avatar

Ville : Bruxelles
Pays : Belgique

Pourquoi mettre dans le titre d'une sous-section "Print(Writer/Reader)" qui laisse sous-entendre qu'une classe PrintReader existe vraiment?

Gnarf o_O !
 

Voir tous les commentaires