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 

Cet exemple aussi montre comment écrire des données vers un fichier. Premièrement, un FileWriter est crée pour se connecter au fichier. Vous voudrez toujours mettre en tampon la sortie en l'emballant[wrapping it] dans un BufferedWriter (essayez de retirer cet emballage pour voir l'impact sur les performances le tampon tend à accroître dramatiquement les performance des opérations d'E/O). Puis le formatage est changé en un PrintWriter. Le fichier de données ainsi crée est lisible comme un fichier texte normal.

Comme les lignes sont écrites vers le fichier, les numéros de lignes sont ajoutés. Notez que LineNumberInputStream n'est pas utilisé, parce que c'est une classe idiote et que vous n'en avez pas besoin. Comme il est montré ici, il est superficiel de garder trace de vos propres numéros de lignes.

Quand le flux d'entrée épuisé, readLine( ) renvoie null. Vous verrez close( ) explicite pour out1, car si vous ne faites pas appel à close( ) pour tous vos fichiers de sortie, vous pourrez découvrir que les tampons ne seront pas libérés sans qu'ils seront incomplets.

Flux de sortie

Les deux types de flux de sortie sont séparés par la manière dont ils écrivent les données : un les écrit pour une consommation humaine, l'autre les écrit pour une reacquisition par un DataInputStream. Le RandomAccessFile se tient seul, bien que son format de données soit compatible avec DataInputStream et DataOutputStream.

5. Stocker et récupérer des données

Un PrintWriter formate les données afin qu'elles soient lisibles par un humain. Cependant, pour sortir des données qui puissent être récupérées par un autre flux, on utilise un DataOutputStream pour écrire les données et un DataInputStream pour récupère les données. Bien sûr, ces flux pourraient être n'importe quoi, mais ici c'est un fichier qui est employé, mis en mémoire tampon pour à la fois lire et écrire. DataOutputStream et DataInputStream sont orientés-byte et nécessitent ainsi des InputStreams and OutputStreams.

Si vous employez un DataOutputStream pour écrire les données, alors Java se porte garant de l'exacte récupération des données en employant un DataInputStream — sans se soucier du type de plate-forme qui écrit et lit les données. Ce qui est incroyablement valable, comme chacun sait ayant passé du temps a s'inquiéter de la distribution de donnée à des plates-formes spécifiques. Ce problème disparaît si l'on a Java sur les deux plates-formes [58].

Notez que les caractères de la chaîne de caractère sont écrit en utilisant à la fois writeChars() et writeBytes(). Quand vous exécuterez le programme, vous découvrirez que writeChars() donne en sortie des caractères Unicode 16-bit. Lorsque l'on lit la ligne avec readLine(), vous remarquerez qu'il y a un espace entre chaque caractère, à cause du byte (ndt : octet ?) supplémentaire inséré par Unicode. Comme il n'y a pas de méthode complémentaire « readChars » dans DataInputStream, vous êtes coincés à retirer ces caractères un par un avec readChar(). Ainsi pour l'ASCII, il est plus facile d'écrire les caractères sous la forme de bytes suivit par un saut de ligne; employez alors readLine() pour relire les bytes comme des lignes régulières ASCII.

Le writeDouble() stocke les nombres double pour le flux et le readDouble() complémentaire les récupère (il y a des méthodes similaires pour lire et écrire les autres types). Mais pour que n'importe quelle méthode de lecture fonctionne correctement, vous devrez connaître l'emplacement exact des éléments de donnée dans le flux, puisqu'il serait possible de lire les double stockés comme de simple séquences de bytes, ou comme des chars, etc. Donc vous devrez soit avoir un format fixé pour les données dans le fichier ou des informations supplémentaires devront êtres stockés dans le fichier et que vous analyserez pour déterminer l'endroit où les données sont stockées.

6. Accés aléatoire en lecture et écriture aux fichiers

Comme il a été noté précédemment, le RandomAccessFile est presque totalement isolé du reste de la hiérarchie d'E/S, protègé par le fait qu'il implémente les interfaces DataInput et DataOutput. Donc vous ne pouvez l'associer avec un des point des sous-classes InputStream et OutputStream. Quoiqu'il pourrait sembler raisonnable de traiter un ByteArrayInputStream comme un élément d'accès aléatoire, vous pouvez employer un RandomAccessFile pour ouvrir simplement un fichier. Vous devez supposer qu'un RandomAccessFile est correctement mis en mémoire tampon puisque vous ne pouvez pas ajouter cela.

La seule option disponible est dans le second argument du constructeur : vous pouvez ouvrir un RandomAccessFile pour lire (« r ») ou lire et écrire (« rw »).

Utiliser un RandomAccessFile est comme utiliser une combinaison de DataInputStream et DataOutputStream (parce que cela implémente les interfaces équivalentes). En plus, vous pouvez remarquer que seek() est utilisé pour errer dans le fichier et changer une des valeurs.

Un bogue ?

Si vous regardez la partie 5, vous verrez que les données sont écrites avant le texte. C'est à cause d'un problème qui a été introduit dans Java 1.1 ( et persiste dans Java 2) qui apparaît vraiment comme un bogue pour moi, mais j'en ai rendu compte et les debogueurs de JavaSoft ont dit que c'était la manière dont il était supposé fonctionner (pourtant, le problème n'apparaissait pas dans Java 1.0, ce qui me rend suspicieux). Le problème est montré dans le code suivant :

//: c11:IOProblem.java
// Problème dans Java 1.1 et supérieur.
import java.io.*;

public class IOProblem {
  // Lance les exeptions vers la console :
  public static void main(String[] args)
  throws IOException {
    DataOutputStream out =      new DataOutputStream(
        new BufferedOutputStream(
          new FileOutputStream("Data.txt")));
    out.writeDouble(3.14159);
    out.writeBytes("C'était la valeur de pi\n");
    out.writeBytes("C'est pi/2:\n");
    out.writeDouble(3.14159/2);
    out.close();

    DataInputStream in =      new DataInputStream(
        new BufferedInputStream(
          new FileInputStream("Data.txt")));
    BufferedReader inbr =      new BufferedReader(
        new InputStreamReader(in));
    // Les doubles écrit AVANT la ligne de texte
    // sont renvoyés correctement :
    System.out.println(in.readDouble());
    // Lit le lignes du texte :
    System.out.println(inbr.readLine());
    System.out.println(inbr.readLine());
    // Tenter de lire les doubles après la ligne
    // produit une eexeption de fin de ligne :
    System.out.println(in.readDouble());
  }
} ///:~

Il apparaît que tout ce que vous écrivez après un appel à writeBytes() n'est pas récupérable. La réponse est apparament la même que la réponse à la vieille blague de vaudeville : « Docteur, cela fait mal quand je fais cela ! » « Ne fait pas cela ! ».

Flux Piped

Les PipedInputStream, PipedOutputStream, PipedReader et PipedWriter sont mentionnés de manière brève dans ce chapitre. Ce qui n'insinue pas qu'il ne sont pas utiles, mais leur importance n'est pas évidente jusqu'à ce que vous ayez commencé a comprendre le multithreading, étant donné quel les flux piped sont employés pour communiquer entre les threads. Ceci est abordé avec un exemple au chapitre 14.

Standard E/S

Le terme d'E/S standard se réfere au concept d'Unix (qui est reproduit sous une certaine forme dans Windows et bien d'autres systèmes d'exploitations) d'un simple flux d'information qui est utilisé par un programme. Toutes les entrées du programme peuvent provenir d'une entrée standard, toutes ses sorties peuvent aller vers une sortie standard, et tous les messages d'erreur peuvent être envoyés à une erreur standard. L'importance de l'E/S standard est que le programme peut être facilement mis en chaîne simultanément et la sortie standard d'un programme peut devenir l'entrée standard pour un autre programme. C'est un outil puissant.

Lire depuis une entrée standard

Suivant le modèle d'E/S standard, Java possède System.in, System.out, et System.err. Tout au long de ce livre vous avez vu comment écrire vers une sortie standard en utilisant System.out, qui est déjà pré-envellopé comme un objet PrintStream. System.err est semblable a PrintStream, mais System.in est un InputStream brut, sans emballage. Ceci signifie que bien que vous pouvez utiliser System.out et System.err immédiatement, System.in doit être envellopé avant de pouvoir lire depuis celui-ci.

Typiquement, vous désirerez lire l'entrée une ligne à la fois en utilisant readLine(), donc vous devrez envelloper System.in dans un BufferedReader. Pour cela, vous devrez convertir System.in en un Reader par l'usage d'InputStreamReader. Voici un exemple qui fait simplement écho de chaque ligne que vous tapez :

//: c11:Echo.java
// Comment lire depuis l'entrée standard.
import java.io.*;

public class Echo {
  public static void main(String[] args)
  throws IOException {
    BufferedReader in =        new BufferedReader(
          new InputStreamReader(System.in));
    String s;
    while((s = in.readLine()).length() != 0)
      System.out.println(s);
    // Une ligne vide met fin au programme.
  }
} ///:~

Le sens de l'instruction d'exception est que readLine() peut lancer une IOException. Notez que pourra généralement être mit en tampon, comme avec la majorité des fluxs

Modifier System.out en un PrintWriter

System.out est un PrintStream, qui est un OutputStream. PrintWriter a un constructeur qui prend un OutputStream comme argument. Ainsi, si vous le désirez vous pouvez convertir System.out en un PrintWriter en utilisant ce constructeur :

//: c11:ChangeSystemOut.java
// Tourne System.out en un PrintWriter.
import java.io.*;

public class ChangeSystemOut {
  public static void main(String[] args) {
    PrintWriter out =
      new PrintWriter(System.out, true);
    out.println("Hello, world");
  }
} ///:~

Il est important d'utiliser la version à deux arguments du constructeur PrintWriter et de fixer le deuxième argument a true afin de permettre un vidage automatique, sinon vous ne verriez pas la sortie.

Réorienter l'E/S standard

La classe Java System vous permet de rediriger l'entrée, la sortie, et l'erreur standard des flux d'E/S en employant un simple appel aux méthodes statiques :

setIn(InputStream) setOut(PrintStream) setErr(PrintStream)

Réorienter la sortie est particulierement utile si vous commencez soudainement a créer une grande quantité de sortie sur l'écran et qu'il défile jusqu'à la fin plus vite que vous ne pouvez le lire. [59] Réorienter l'entrée est précieux pour un programme en ligne de commande dans lequel vous désirez tester un ordre d' entrée-utilisateur particulièr à plusieurs reprises. Voici un exemple simple qui montre l'utilisation de ces méthodes :

//: c11:Redirecting.java
// Demonstration de reorientation d'E/S standard.
import java.io.*;

class Redirecting {
  // Lance les exeptions vers la console :
  public static void main(String[] args)
  throws IOException {
    BufferedInputStream in =
      new BufferedInputStream(
        new FileInputStream(
          "Redirecting.java"));
    PrintStream out =      new PrintStream(
        new BufferedOutputStream(
          new FileOutputStream("test.out")));
    System.setIn(in);
    System.setOut(out);
    System.setErr(out);

    BufferedReader br =
      new BufferedReader(
        new InputStreamReader(System.in));
    String s;
    while((s = br.readLine()) != null)
      System.out.println(s);
    out.close(); // Rappelez-vous de ça !
  }
} ///:~

Ce programme attache la sortie standard à un ficher, et redirige la sortie standard et l'erreur standard vers un autre fichier.

La redirection d'E/S manipule les fluxs de bytes, mais pas les fluxs de caractères, ainsi InputStreams et OutputStreams sont plus utilisés que les Readers et Writers.

Compression

La librairie (ndt : ou bibliothèque) d'E/S Java contient des classes pour supporter la lecture et l'écriture de flux dans des formats compressés. Ceux-ci sont envellopés autour des classes existantes d'E/S pour fournir des fonctionnalités de compression.

Ces classes ne sont pas dérivée des classes Reader et Writer, mais à la place font partie des hiérarchies d'InputStream et OutputStream. Ceci parcque la libraire de compression fonctionne avec des bytes, pas des caractères. Cependant, vous serez parfois forcés de mixer les deux types de fluxs. (Rappellez-vous que vous pouvez utiliser InputStreamReader et OutputStreamWriter pour fournirune conversion facile entre un type et un autre.)

Classe de compression Fonction
CheckedInputStream GetCheckSum() fait une checksum (vérification du nombre de bits transmis afin de deceler des erreurs de transmition) pour n'importe quel InputStream (non pas une simple décompression).
CheckedOutputStream GetCheckSum() fait une checksum pour n'importe quel OutputStream (non pas une simple compression).
DeflaterOutputStream Classe de base pour les classes de compression.
ZipOutputStream Un DeflaterOutputStream qui compresse les données au format Zip.
GZIPOutputStream Un DeflaterOutputStream qui compresse les données au format GZIP.
InflaterInputStream Classe de base pour les classes de décompression.
ZipInputStream Un InflaterInputStream qui décompresse les données qui sont stockées au format Zip.
GZIPInputStream Un InflaterInputStream qui décompresse les données qui sont stockées au format GZIP.

Bien qu'il y ait de nombreux algorithmes de compression, Zip et GZIP sont peut-être ceux employés le plus courament. Ainsi vous pouvez facilement manipuler vos données compressées avec les nobreux outils disponibles pour écrire et lire ces formats.

Compression simple avec GZIP

L'interface GZIP est simple et est ainsi la plus appropriée quand vous avez un simple flux de donnée que vous désirez compresser (plutôt qu'un récipient (container) de pièces différentes de données. Voici un exemple qui compresse un simple fichier :

//: c11:GZIPcompress.java
// Utilise la compression GZIP pour compresser un fichier
// dont le nom est passé en ligne de commande.
import java.io.*;
import java.util.zip.*;

public class GZIPcompress {
  // Lance les exceptions vers la console :
  public static void main(String[] args)
  throws IOException {
    BufferedReader in =      new BufferedReader(
        new FileReader(args[0]));
    BufferedOutputStream out =      new BufferedOutputStream(
        new GZIPOutputStream(
          new FileOutputStream("test.gz")));
    System.out.println("Writing file");
    int c;
    while((c = in.read()) != -1)
      out.write(c);
    in.close();
    out.close();
    System.out.println("Reading file");
    BufferedReader in2 =      new BufferedReader(
        new InputStreamReader(
          new GZIPInputStream(
            new FileInputStream("test.gz"))));
    String s;
    while((s = in2.readLine()) != null)
      System.out.println(s);
  }
} ///:~

L'emploi des classes de compression est simple — vous envellopez simplement votre flux de sortie dans un GZIPOutputStream ou un ZipOutputStream et votre flux d'entrée dans un GZIPInputStream ou un ZipInputStream. Tout le reste étant de l'écriture normale d'entrée et de sortie. C'est un exemple de mélange de fluxs orientés-char avec des fluxs orientés-byte : in utilise la classe Reader, vu que le constructeur de GZIPOutputStream peut seulement accepter un objet OutputStream, et non pas un objet Writer. Quand le fichier est ouvert, le GZIPInputStream est convertit en un Reader.

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