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 |
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.
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.
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.
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.
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 :
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 ! ».
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.
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.
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 :
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
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 :
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.
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 :
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.
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.
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 :
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.