"flots" d’entrée/sortie

images/orangebelt.png

Comme pour beaucoup de logithèques le challenge à relever pour Java était de mettre au point des principes utilisables sur des plate-formes de nature très différentes.

Les systèmes d’entrée/sortie (abréviation "E/S") disponibles en standard en Java sont essentiellement:

Ce chapitre aborde les flots d’E/S ("stream") qui sont largement utilisés et ressemblent aux E/S UNIX primordiales. Les principes de java.nio seront abordés ultérieurement dans un autre chapitre.

Le modèle des "flots"

schema: io stream

Un flot n’est ouvert que dans un sens -ici en lecture- (l'écriture se fera par l’ouverture d’un autre flot: ce modèle ne connaît pas les E/S bidirectionnelles).

Chaque opération de read "consomme" des données

Inversement chaque opération de write dans un OutputStream "poussera" des données.

Flots associés à une ressource (node streams)

schema: io nodes

Le même modèle peut s’appliquer à des ressources très différentes qui produisent des octets que le stream va consommer.

Filtres

schema: io filters

Ici les octets peuvent venir de différents dispositifs:

InputStream in ;
// n'importe ...
// par exemple : in = new FileInputStream(filename) ;

new DataInputStream(new BufferedInputStream(in)) :

On peut ainsi créer des dispositifs d’E/S à la demande: par exemple un ObjectInputStream (pour lire directement des objets java) sur un flot compressé, crypté, bufférisé sur une ligne de communication (flot TCP/IP sur connecteur réseau "socket").

flots d’octets: InputStream, OutputStream

Les flots fondamentaux lisent ou écrivent des octets. Diverses méthodes d’assez bas niveau permettent des opérations sur plusieurs octets.

java.io.InputStream
   int read() throws IOException
   // lit un octet comme un int
   // + autres read(°°°) de bas niveau

java.io.OutputStream
   void write(int byte) throws IOException
   // écrit un octet avec un argument sous forme de int
   // + autres write(°°°) de bas niveau

Dans les deux classes on a:

   void close() throws IOException

Flots de caractères: Reader, Writer

Comme le codage fondamental des caractères en Java est UTF16 (deux octets) Les flots de base pour gérer le type char sont différents de InputStream, OutputStream.

java.io.Reader
   int read() throws IOException
   // lit un "char" dans un "int"
   // + autres read(°°°) de bas niveau

java.io.Writer
   void write(int character) throws IOException
   // écrit un "char" en passant par un "int"
   // + autres write(°°°) de bas niveau

et, bien sûr, d’autres méthodes comme close().

Comparaison des flots par ressource

InputStream Reader
tableau mémoire ByteArrayInputStream CharArrayReader StringReader
"Pipe" PipedInputStream PipedReader
fichier FileInputStream FileReader (classe "de confort")
"fabriques": filtrées par InputStreamReader
java.lang.Process getInputStream()
java.net.Socket getInputStream()
java.net.URL openStream()

Chaque fois qu’un flot lit des données caractères venu du monde "extérieur" à Java (fichier, socket ,…) il n’est pas garanti que le codage soit de l’UTF16! On a besoin d’un InputStreamReader pour opérer des conversions appropriées.

Conversions octets/caractères

Les classes InputStreamReader et OutputStreamWriter permettent de connecter des flots d’octets et des flots de caractères en opérant des conversions.

Conversions de caractères. 

   // se reformule avec un try(création AutoCloseable)
   try {
      InputStream fis = new FileInputStream("unfichier.txt") ;
      Reader ir = new InputStreamReader(fis,"ISO-8859-1") ;
      //.....
   } catch (FileNotFoundException exc) {//....
   } catch (UnsupportedEncodingException exc) {//....
   }

Le second argument est une chaîne désignant le type de codage( «ISO-8859-1» ou «ISO-8859-15» pour la plupart des pays d’Europe de l’ouest, «Cp1252» sous MS-Windows). Les liste des codages se trouve dans la documentation docs/guide/internat/encoding.doc.html -voir aussi l’utilitaire native2ascii ou la documentation de java.nio.charset-.

[Note]

Il existe un codage de nom UTF8 pour coder les caractères UNICODE sur 8 bits. Il est de plus en plus "natif" sur des systèmes dérivés d’UNIX/LINUX.

Pour ces deux classes il existe un constructeur avec un seul argument: le mode de codage par défaut résultant de la configuration du système d’exploitation est alors retenu.

Utilisation de filtres

Les constructeurs de classes "filtre" ont toujours un autre flot en paramètre:

public BufferedInputStream(InputStream in) ...
public DataInputStream(InputStream in)...

C’est en enchainant le passage des flots aux constructeurs qu’on combine les comportements.

   BufferedInputStream bis = new BufferedInputStream(
      new FileInputStream (filename)) ;
   DataOutputStream dos = new DataOuputStream(
      new BufferedOutputStream (
         new FileOutputStream (filename))) ;

Certaines classes filtres ont des méthodes spécifiques (comme writeInt pour DataOutputStream). Noter l’utilisation de flush() qui est transmis au BufferedOutputStream dans l’enchainement des filtres.

Utilisation de filtres. 

   try {
      dos.writeInt(235);
      dos.writeDouble(Math.PI);
      dos.writeUTF("Une chaîne avec des caractères accentués") ;
      // chaîne écrite en UTF8
      dos.flush() ; // pour le BufferedOutputStream
      // ....
   } finally {
      dos.close() ; // ferme toute la chaîne des flots
      // sauf si try initial avec AutoCloseable
   } catch(IOException exc) {
      // au rapport!
   }

Usage simple des Paths

images/bluebelt.png

import java.nio.file.* ;
import java.nio.charset.* ;
....
   String home =System.getProperty("user.home") ; // si on a les droits!!! (pas garanti)
   //noter la désignation indépendante du Système d'exploitation
   Path file = Paths.get(home, "java", "todolist.txt");
   // try avec AutoCloseable
   // noter l'utilisation des classes Files et StandardCharsets
   try (Reader output = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
      // code
   }

--exercice--

[Note]

Voir également des utilitaires généraux dans java.nio.file.Files