LECON 314


Les flux d'entrées / sorties (2/2)
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 !

Je ne vais pas vous gâcher la surprise... Alors, on y va ?
Sommaire du chapitre :

  • Les objets CharArray(Writer/Reader) et String(Writer/Reader)
  • les classes File(Writer/Reader) et Print(Writer/Reader)
  • Du renouveau chez les flux : le package java.nio

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.

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.

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 !

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 :


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.

En avant pour : le jeu du pendu !


0 comments to "LECON 314"

Post a Comment

Powered by Blogger.

About This Blog

Aller au debut de la page