IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java

  Chapitre 11 - Le système d’E/S de Java

pages : 1 2 3 4 5 6 7 8 9 10 

Sources & Récipients: Classe Java 1.0 Classe Java 1.1 correspondante
InputStream Reader convertisseur : InputStreamReader
OutputStream Writer convertisseur : OutputStreamWriter
FileInputStream FileReader
FileOutputStream FileWriter
StringBufferInputStream StringReader
(pas de classe correspondante) StringWriter
ByteArrayInputStream CharArrayReader
ByteArrayOutputStream CharArrayWriter
PipedInputStream PipedReader
PipedOutputStream PipedWriter

En général, vous constaterez que les interfaces pour les deux différentes hiérarchies sont semblables sinon identiques.

Modifier le comportement du flux

Pour les InputStreams et OutputStreams, les flux sont adaptés à des usages particuliers en utilisant des sous-classes « décoratives » de FilterInputStream et FilterOutputStream. La hiérarchie de classe Reader et Writer poursuit l'usage de ce concept — mais pas exactement.

Dans le tableau suivant, la correspondance est une approximation plus grossière que dans la table précédente. La différence est engendrée par l'organisation de la classe : Quand BufferedOutputStream est une sous-classe de FilterOutputStream, BufferedWriter n'est pas une sous-classe de FilterWriter (laquelle, bien qu'elle soit abstract, n'a pas de sous-classe et donc semble avoir été mise dedans de manière à réserver la place ou simplement de manière à ce que vous ne sachiez pas où elle se trouve). Cependant, les interfaces pour les classes est plutôt un combat terminé.

Filtres : classe Java 1.0 Classe correspondante en Java 1.1
FilterInputStream FilterReader
FilterOutputStream FilterWriter (classe abstract sans sous-classe)
BufferedInputStream BufferedReader (a aussi readLine())
BufferedOutputStream BufferedWriter
DataInputStream Utilise DataInputStream (sauf quand vous voulez utiliser readLine(), alors vous devez utiliser un BufferedReader)
PrintStream PrintWriter
LineNumberInputStream LineNumberReader
StreamTokenizer StreamTokenizer (utilise un constructeur qui prend un Reader à la place)
PushBackInputStream PushBackReader

Il y a un sens qui est tout à fait clair : Chaque fois que vous voulez utiliser readLine(), vous ne devrez plus le faire avec un DataInputStream (ceci recevant un message de depréciation au moment de la compilation), mais utiliser à la place un BufferedReader. À part cela, DataInputStream est pourtant l'élément « préféré » de la bibliothèque d'E/S.

Pour faire la transition vers l'emploi facile d'un PrintWriter, il possède des constructeurs qui prennent n'importe quel objet OutputStream, aussi bien que des objets Writer. Cependant, PrintWriter n'a pas plus de support pour formater comme le faisait PrintStream ; les interfaces sont de fait les mêmes.

Le constructeur de PrintWriter possède également une option pour effectuer le vidage automatique de la mémoire [automatic flushing], lequel se produit après chaque println() si le drapeau du constructeur est levé dans ce sens.

Les classes inchangées

Certaines classes ont été laissées inchangées entre Java 1.0 et Java 1.1 :

Les classes de Java 1.0 qui n'ont pas de classes correspondantes en Java 1.1
DataOutputStream
File
RandomAccessFile
SequenceInputStream

DataOutputStream, en particulier, est utilisé sans modification, donc pour stocker et retrouver des données dans un format transportable vous utiliserez les hiérarchies InputStream et OutputStream.

Et bien sûr : L'accès aléatoire aux fichiers (RandomAccessFile)

RandomAccessFile est employé pour les fichiers dont la taille de l'enregistrement est connue, de sorte que vous pouvez bouger d'un enregistrement à un autre en utilisant seek(), puis lire ou changer les enregistrements. Les enregistrements n'ont pas forcément la même taille ; vous devez seulement être capable de déterminer de quelle grandeur ils sont et où ils sont placés dans le fichier.

D'abord il est un peu difficile de croire que RandomAccessFile ne fait pas partie de la hiérarchie d'InputStream ou d'OutputStream. Cependant, il n'y a pas d'association avec ces hiérarchies autre que quand il arrive de mettre en œuvre les interfaces DataInput et DataOutput (qui sont également mises en œuvre par DataInputStream et DataOutputStream). Elle n'utilise même pas la fonctionnalité des classes existantes InputStream et OutpuStream — il s'agit d'une classe complétement différente, écrite en partant de zéro, avec toutes ses propres méthodes (pour la plupart native). Une raison à cela pouvant être que RandomAccessFile a des comportements essentiellement différents des autres types d'E/S, dès qu'il est possible de se déplacer en avant et en arrière dans un fichier. De toute façon, elle reste seule, comme un descendant direct d'Object.

Essentiellement, un RandomAccessFile fonctionne comme un DataInputStream collé ensemble avec un DataOutputStream, avec les méthodes getFilePointer() pour trouver où on se trouve dans le fichier, seek() pour se déplacer vers un nouvel emplacement dans le fichier, et length() pour déterminer la taille maximum du fichier. En complément, les constructeurs requièrent un deuxième argument (identique à fopen() en C) indiquant si vous effectuez de manière aléatoire une lecture (« r ») ou une lecture et écriture (« rw »). Il n'y a pas de ressource pour les fichiers en lecture seule, ce qui pourrait suggérer que RandomAccessFile aurait mieux fonctionné s'il se trouvait hérité de DataInputStream.

Les méthodes de recherche sont valables seulement dans RandomAccessFile, qui fonctionne seulement avec des fichiers. Le BufferedInputStream permet de marquer « mark() » une position (dont la valeur est tenue dans une seule variable interne) et d'annuler cette position « reset() », mais c'est limité et pas très pratique.

L'usage typique des flux d'E/S

Bien que pouvez combiner les classes de flux d'E/S de différentes manières, vous utiliserez probablement quelques combinaisons. L'exemple suivant pourra être employé comme une référence de base ; il montre la création et l'utilisation de configurations d'E/S typiques. Notez que chaque configuration commence par un commentaire avec numéro et titre qui correspondent aux titres des paragraphes suivant et fournissant l'explication approprié.

//: c11:IOStreamDemo.java
// Configurations typiques de flux d'E/S.
import java.io.*;

public class IOStreamDemo {
  // Lance les exeptions vers la console :
  public static void main(String[] args)
  throws IOException {
    // 1. Lecture d'entrée par lignes :
    BufferedReader in =      new BufferedReader(
        new FileReader("IOStreamDemo.java"));
    String s, s2 = new String();
    while((s = in.readLine())!= null)
      s2 += s + "\n";
    in.close();

    // 1b. Lecture d'entrée standard :
    BufferedReader stdin =      new BufferedReader(
        new InputStreamReader(System.in));      
    System.out.print("Enter a line:");
    System.out.println(stdin.readLine());

    // 2. Entrée depuis la mémoire
    StringReader in2 = new StringReader(s2);
    int c;
    while((c = in2.read()) != -1)
      System.out.print((char)c);

    // 3. Entrée de mémoire formattée
    try {
      DataInputStream in3 =        new DataInputStream(
          new ByteArrayInputStream(s2.getBytes()));
      while(true)
        System.out.print((char)in3.readByte());
    } catch(EOFException e) {
      System.err.println("End of stream");
    }

    // 4. Sortie de fichier
    try {
      BufferedReader in4 =        new BufferedReader(
          new StringReader(s2));
      PrintWriter out1 =        new PrintWriter(
          new BufferedWriter(
            new FileWriter("IODemo.out")));
      int lineCount = 1;
      while((s = in4.readLine()) != null )
        out1.println(lineCount++ + ": " + s);
      out1.close();
    } catch(EOFException e) {
      System.err.println("End of stream");
    }

    // 5. Stockage et recupération de donnée
     try {
      DataOutputStream out2 =        new DataOutputStream(
          new BufferedOutputStream(
            new FileOutputStream("Data.txt")));
      out2.writeDouble(3.14159);
      out2.writeChars("That was pi\n");
      out2.writeBytes("That was pi\n");
      out2.close();
      DataInputStream in5 =        new DataInputStream(
          new BufferedInputStream(
            new FileInputStream("Data.txt")));
      BufferedReader in5br =        new BufferedReader(
          new InputStreamReader(in5));
      // Doit utiliser DataInputStream pour des données :
      System.out.println(in5.readDouble());
      // Peut maintenant employer le readLine():
      System.out.println(in5br.readLine());
      // Mais la ligne ressort bizzarement.
      // Celle crée avec writeBytes est OK:
      System.out.println(in5br.readLine());
    } catch(EOFException e) {
      System.err.println("End of stream");
    }

    // 6. Lecture/écriture par accès aléatoire aux fichiers [Reading/writing random access files]
    RandomAccessFile rf =      new RandomAccessFile("rtest.dat", "rw");
    for(int i = 0; i < 10; i++)
      rf.writeDouble(i*1.414);
    rf.close();

    rf =      new RandomAccessFile("rtest.dat", "rw");
    rf.seek(5*8);
    rf.writeDouble(47.0001);
    rf.close();

    rf =      new RandomAccessFile("rtest.dat", "r");
    for(int i = 0; i < 10; i++)
      System.out.println(
        "Value " + i + ": " +
        rf.readDouble());
    rf.close();
  }
} ///:~

Voici les descriptions pour les sections numérotées du programme :

Flux d'Entrée

La partie 1 à 4 démontre la création et l'utilisation des flux d'entrée. La partie 4 montre aussi l'emploi simple d'un flux de sortie.

1. Entrée en tampon du fichier [Buffered input file]

Afin d'ouvrir un fichier pour l'entrée de caractères, on utilise un FileInputReader avec un objet String ou File comme nom de fichier. Pour la vitesse, on désirera que le fichier soit mis en mémoire tampon alors on passera la référence résultante au constructeur à un BufferedReader. Puisque BufferedReader fournit aussi la méthode readLine( ), qui est notre objet final et l'interface depuis laquelle on lit. Quand on cherche la fin du fichier, readLine( ) renverra null qui sera utilisé pour sortir de la boucle while.

Le String s2 est utilisé pour accumuler le contenu entier du fichier (incluant les nouvelles lignes qui doivent être ajoutées puisque readLine( ) les enlève). s2 est ensuite employé dans la dernière partie de se programme. Enfin, close( ) est appelé pour fermer le fichier. Techniquement, close( ) sera appelé au lancement de finalize(), et ceci est supposé se produire (que le garbage collector se mette en route ou pas) lors de la fermeture du programme. Cependant, ceci a été inconséquemment implémenté, c'est pourquoi la seule approche sûre est d'appeler explicitement close( ) pour les fichiers.

La section 1b montre comment envelopper System.in afin de lire l'entrée sur la console. System.in est un DataInputStream et BufferedReader nécessite un argument Reader, voilà pourquoi InputStreamReader est introduit pour effectuer la traduction.

2. Entrée depuis la mémoire

Cette partie prend le String s2 qui contient maintenant le contenu entier du fichier et l'utilise pour créer un StringReader. Puis read( ) est utilisé pour lire chaque caractère un par un et les envoie vers la console. Notez que read( ) renvoie le byte suivant sous la forme d'un int et pour cette raison il doit être convertit en char afin de s'afficher correctement.

3. Entrée de mémoire formatée

Pour lire une donnée « formatée », vous utiliserez un DataInputStream, qui est une classe d'E/S orientée-byte (plutôt qu'orientée-char). Ainsi vous devrez utiliser toutes les classes InputStream plutôt que les classes Reader. Bien sur, vous pouvez lire n'importe quoi (du genre d'un fichier) comme des bytes en utilisant les classes InputStream, mais ici c'est un String qui est utilisé. Pour convertir le String en un tableau de bytes, ce qui est approprié pour un ByteArrayInputStream, String possède une méthode getBytes()pour faire le travail. A ce stade, vous avez un InputStream adéquat pour porter un DataInputStream.

Si on lit les caractères depuis un DataInputStream un byte à chaque fois en utilisant readByte( ), n'importe quelle valeur de byte donne un résultat juste donc la valeur de retour ne peut pas être employée pour détecter la fin de l'entrée. À la place, on peut employer la méthode avalaible( ) pour découvrir combien de caractères sont encore disponibles. Voici un exemple qui montre comment lire un fichier byte par byte :

//: c11:TestEOF.java
// Test de fin de fichier
// En lisant un byte a la fois.
import java.io.*;

public class TestEOF {
  // Lance les exeptions vers la console :
  public static void main(String[] args)
  throws IOException {
    DataInputStream in =
      new DataInputStream(
       new BufferedInputStream(
        new FileInputStream("TestEof.java")));
    while(in.available() != 0)
      System.out.print((char)in.readByte());
  }
} ///:~

Notons qu'available() fonctionne différemment en fonction du type de ressource depuis laquelle on lit; c'est littéralement « le nombre de bytes qui peuvent être lus sans blocage. » Avec un fichier cela signifie le fichier entier, mais avec une autre sorte d'entrée cela ne pourra pas être possible, alors employez le judicieusement.

On peut aussi détecter la fin de l'entrée dans des cas comme cela en attrapant une exception. Cependant, l'emploi des exeptions pour le contrôle du flux est considéré comme un mauvais emploi de cette caractéristique.

Ce livre a été écrit par Bruce Eckel ( télécharger la version anglaise : Thinking in java )
Ce chapitre a été traduit par Armel Fortun ( groupe de traduction )
télécharger la version francaise (PDF) | Commandez le livre en version anglaise (amazon) | télécharger la version anglaise
pages : 1 2 3 4 5 6 7 8 9 10 
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java