Entrées/Sorties


Abstraction

Abstraction

Un programme intéragie avec le monde extérieur:

  • ses actions dépendent des paramètre reçus, des ordres reçus, des événements se produisant pendant son exécution, etc…
  • les calculs effectués par le programme servent à déclancher des événements ou sont transmis à l’utilisateur ou à d’autres programmes.

 

Définition : On appelle Entrées/Sorties (E/S) l’ensemble des actions qui servent respectivement à recevoir et produire des données quel que soit le type d’interaction utilisée.

Types d’interactions

Éditer un fichier Lire un fichier

Envoyer un message Recevoir un message

Ajouter une valeur Consulter une valeur

I/O Stream

Définition : Un flux d’entrée/sortie (I/O Stream en anglais) permet d’encapsuler les envois et les réceptions de données de façon à cacher le support utilisé. Ainsi un même code utilisera indifféremment : un fichier, le réseau ou la mémoire.

                    Flux de Sortie       Flux d’entrée
Ecrire                            Lire

                    Flux de Sortie       Flux d’entrée
Ecrire                            Lire

                    Flux de Sortie       Flux d’entrée
Ecrire                            Lire

                    Flux de Sortie       Flux d’entrée
Ecrire                            Lire

Les types de flux

Les flux d’entrée/sortie sont des classes permettant d’écrire ou de lire des octets ou des caractères (depuis ou vers une entité quelconque). Ce sont des classes abstraites!!

Input Output
Byte (Octet) InputStream OutputStream
Character Reader Writer

                                                              OutputStream       InputStream
Ecrire des octets                            Lire des octets

                                                                                      Writer                   Reader
Ecrire des caractères                            Lire des caractères

Lecture d’Octets avec InputStream

documentation

int read() throws IOException lecture d’un octet

  • bloque jusqu’a ce q’un octet soit disponible
  • l’octet lu est retourné sous forme d’un entier
  • retourne -1 si la fin du flux est atteinte

 

void close() throws IOException : fermeture du flux.

Lecture d’Octets avec InputStream

int read(byte[] b, int off, int len) throws IOException

  • bloque jusqu’à ce q’un octet soit disponible
  • les octets lus (len au maximum) sont placés dans le tableau (a partir de la position off)
  • retourne le nombre d’octets lus
  • retourne -1 si aucun octet n’a été lu et si la fin du flux est atteinte

 

int read(byte[] b) throws IOException: identique à read(b, 0, b.length)

Ecriture d’Octets avec OutputStream

documentation

void write(int b) throws IOException : écriture d’un octet

void write(byte[] tab) throws IOException : écriture d’un tableau d’octets

void flush() throws IOException : vidage des buffers en forçant l’écriture.

void close() throws IOException : fermeture du flux.

Exemples


// Récupération du flux
OutputStream os = ?????? ;

// Utilisation du flux
for (byte b=0; b<5 ;b++)
{
    os.write(b);
    os.write(2*b+1);
}
// Fermeture du flux
os.close();
                        

Exemples


// Récupération du flux
InputStream is = ?????? ;
// Utilisation du flux
int x,y ;
while ((x=is.read())!=-1) {
    y = is.read();
    dessin.trace(x,y);
}
// Fermeture du flux
is.close();
                        

Lecture de caractères avec Reader

documentation

int read() throws IOException lecture d’un caractère

  • bloque jusqu’a ce q’un caractère soit disponible
  • le caractère lu est retourné sous forme d’un entier
  • retourne -1 si la fin du flux est atteinte

 

void close() throws IOException : fermeture du flux.

Lecture de caractères avec Reader

int read(char[] cbuf, int off, int len) throws IOException

  • Bloque jusqu’a ce q’un caractère soit disponible
  • les caractères lus (len au maximum) sont placés dans le tableau (à partir de la position off)
  • retourne le nombre de caractères lus
  • retourne -1 si aucun caractère n’a été lu et si la fin du flux est atteinte

 

int read(char[] cbuf) throws IOException: identique à read(cbuf, 0, cbuf.length)

Ecriture de caractères avec Writer

documentation

void write(int b) throws IOException : écriture d’un caractère

void write(char[] cbuf) throws IOException : écriture d’un tableau de caractères

void write(String str) throws IOException : écriture d’une chaine de caractères

void flush() throws IOException : vidage des buffers en forçant l’écriture.

void close() throws IOException : fermeture du flux.

Exemples


// Récupération d’un flux
Writer w = ?????? ;
// Utilisation du flux
w.write('a');
w.write('b');
w.write("cdef");
// Fermeture du flux
w.close();
                        

Exemples


// Récupération d’un flux
Reader r = ?????? ;
// Utilisation du flux
StringBuffer buff = new StringBuffer();
int i;
while ((i=r.read())!=-1) {
    buff.append((char)i);
}
// Fermeture du flux
r.close();
                        

Construction de Flux d’E/S

Où sont les flux?

On distingue deux méthodes pour obtenir la référence d’un flux :

  • Par accesseur : certaines classes de Java possèdent des flux pour produire ou recevoir des données. Ceux-ci sont accessibles directement (System.in et System.out) ou au travers d’accesseurs (getInputStream() et getOutputStream()).
  • Par constructeur : pour les objets qui ne possèdent pas directement de flux, Java offre pour les construire des classes spécifiques à chaque support. Toutes ces classes héritent d’un des 4 flux de base : InputStream, OutputStream, Reader ou Writer.

 

Flux standard

La classe System est pourvue de trois attributs de classe :

  • in : un flux qui correspond à l’entrée standard (par défaut le clavier)
  • out : un flux qui correspond à la sortie standard (par défaut la console)
  • err : un flux qui correspond à la sortie d’erreur (par défaut la console)

 


// Lecture sur le clavier
int x = System.in.read();

// Écriture sur la console
System.out.write(x);

Flux standard

Dans le cas de la classe Process les flux permettent d’écrire (resp. de lire) sur l’entrée standard (resp. la sortie standard) du processus.


    // Lancement du programme say (Mac Os)
    Process p = Runtime.getRuntime().exec("say");
    // Récupération du flux d’entrée standard du programme
    // ATTENTION : Pour nous c’est un flux de sortie (out)
    OutputStream os = p.getOutputStream();

    // Utilisation du flux : comme avec tout OutputStream
    os.write("bonjour, comment ça va?".getBytes());

    // Fermeture du flux (=> arrêt du programme beep)
    os.close();

Construction de flux

Source / Destination
(Défini avec un préfixe)
Entrée en Octets
(Hérite de InputStream)
Sortie en Octets
(Hérite de OutputStream)
Tableau d’octets en mémoire ByteArrayInputStream ByteArrayOutputStream
Fichier FileInputStream FileOutputStream
Pipeline entre deux threads PipedInputStream PipedOutputStream
Chaîne de caractères StringBufferInputStream StringBufferOutputStream

Construction de flux

Source / Destination
(Défini avec un préfixe)
Entrée en caractères
(Hérite de Reader)
Sortie en caractères
(Hérite de Writer)
Tableau d’octets en mémoire CharArrayReader CharArrayWriter
Fichier FileReader FileWriter
Pipeline entre deux threads PipedReader PipedWriter
Chaîne de caractères StringReader StringWriter

Exemples


// Récupération du flux
OutputStream os = ??????;

// Utilisation du flux
for (byte b=0; b<5 ;b++)
{
 os.write(b);
 os.write(2*b+1);
}
// Fermeture du flux
os.close();
                     

// Récupération du flux
OutputStream os = new ByteArrayOutputStream();

// Utilisation du flux
for (byte b=0; b<5 ;b++)
{
 os.write(b);
 os.write(2*b+1);
}
// Fermeture du flux
os.close();
                     

// Récupération du flux
OutputStream os = new FileOutputStream("/path/to/file.bin");

// Utilisation du flux
for (byte b=0; b<5 ;b++)
{
    os.write(b);
    os.write(2*b+1);
}
// Fermeture du flux
os.close();

// Récupération du flux
InputStream is = ?????? ;
// Utilisation du flux
int x,y ;
while ((x=is.read())!=-1) {
 y = is.read();
 dessin.trace(x,y);
}
// Fermeture du flux
is.close();

byte[] buf = {0, 1, 0, 2};
// Récupération du flux
InputStream is = new ByteArrayInputStream(buf);
// Utilisation du flux
int x,y ;
while ((x=is.read())!=-1) {
 y = is.read();
 dessin.trace(x,y);
}
// Fermeture du flux
is.close();

// Récupération du flux
InputStream is = new FileInputStream("/path/to/file.bin");
// Utilisation du flux
int x,y ;
while ((x=is.read())!=-1) {
y = is.read();
dessin.trace(x,y);
}
// Fermeture du flux
is.close();

Exemples


// Recupération d’un flux
Writer w = ?????????????????? ;
// Utilisation du flux
w.write('a');
w.write('b');
w.write("cdef");
// Fermeture du flux
w.close();
                     

// Recupération d’un flux
Writer w = new CharArrayWriter();
// Utilisation du flux
w.write('a');
w.write('b');
w.write("cdef");
// Fermeture du flux
w.close();
                     

// Recupération d’un flux
Writer w = new FileWriter("/path/to/file.txt"); ;
// Utilisation du flux
w.write('a');
w.write('b');
w.write("cdef");
// Fermeture du flux
w.close();

// Récupération d’un flux
Reader r = ?????????????????? ;
// Utilisation du flux
StringBuffer buff = new StringBuffer();
int i;
while ((i=r.read())!=-1) {
    buff.append((char)i);
}
// Fermeture du flux
r.close();

char[] buf = {'h', 'e', 'l', 'l', 'o'};
// Récupération d’un flux
Reader r = new CharArrayReader(buf);
// Utilisation du flux
StringBuffer buff = new StringBuffer();
int i;
while ((i=r.read())!=-1) {
    buff.append((char)i);
}
// Fermeture du flux
r.close();

// Récupération d’un flux
Reader r = new FileReader("/path/to/file.txt");
// Utilisation du flux
StringBuffer buff = new StringBuffer();
int i;
while ((i=r.read())!=-1) {
    buff.append((char)i);
}
// Fermeture du flux
r.close();

Les filtres

Les filtres

Définition : Pour ajouter des fonctionnalités aux flux, Java utilise des classes appelées filtre suivant le patron de conception (design-pattern) du décorateur.

Le nom des filtres Java est suffixé par l’un des quatre flux de base :

  • ils héritent de Filter[FluxBase] qui hérite de [FluxBase]
  • ils prennent ce flux comme paramètre de leur constructeur.

 

Pour ajouter des fonctionnalités ils peuvent :

  • redéfinir le fonctionnement des méthodes de base (read, write, …), par polymorphisme l’utilisation est transparente et interchangeable
  • ajouter de nouvelles méthodes (print, writeBoolean, readLine, …)

 

Les filtres

Préfixe Fonctionnalité
InputStream
OutputStream
Reader
Writer
Buffered Mise en tampon
Data Conversion des types primitifs
Object Sérialisation d’objet
Sequence Concaténation de flux
Print Fonctions de formatage print
PushBack Lecture avec remise dans le flux
LineNumber Numérotation des lignes
Digest Vérification par somme de contrôle
Cipher Chiffrage du flux

Attention

InputStreamReader, resp. OutputStreamWriter n’est pas un flux de type InputStream, resp. OutputStream.

C’est un constructeur de flux de type Reader, resp. Writer, à partir d’un flux de type InputStream, resp. OutputStream

 

Compositions de filtres

OutputStream
InputStream

OutputStream os = ?????? ;
os.write((byte)1);
os.write((byte)2);
os.write((byte)3);
os.close();

                   

InputStream is = ?????? ;
while ( (x = is.read()) != -1 )
{
    System.out.println(x);
}
is.close();
                       
FileOutputStream
FileInputStream

OutputStream os =
new FileOutputStream("test.bin");
os.write((byte)1);
os.write((byte)2);
os.write((byte)3);
os.close();

                   

InputStream is =
new FileInputStream("test.bin");
while ( (x = is.read()) != -1 )
{
    System.out.println(x);
}
is.close();
                       
CipherOutputStream
FileOutputStream
FileInputStream
CipherInputStream

OutputStream os =
new CipherOutputStream(
new FileOutputStream("test.bin"),c);
os.write((byte)1);
os.write((byte)2);
os.write((byte)3);
os.close();

                   

InputStream is =
new CipherInputStream(
new FileInputStream("test.bin"), c);
while ( (x = is.read()) != -1 )
{
    System.out.println(x);
}
is.close();
                       
GZIPOutputStream
CipherOutputStream
FileOutputStream
FileInputStream
CipherInputStream
GZIPInputStream

OutputStream os =
new GZIPOutputStream(
new CipherOutputStream(
new FileOutputStream("test.bin"),c)
);
os.write((byte)1);
os.write((byte)2);
os.write((byte)3);
os.close();

                   

InputStream is =
new GZIPInputStream(
new CipherInputStream(
new FileInputStream("test.bin"), c)
);
while ( (x = is.read()) != -1 )
{
    System.out.println(x);
}
is.close();
                       

Filtres personnalisés

Pour un filtre personnalisé destiné à la lecture d’octets : définir une classe qui hérite de FilterInputStream, implémenter un constructeur à un paramètre InputStream is :

  • appeler à la première ligne du constructeur super(is)
  • si besoin enregistrer les autres paramètres dans des attributs

 

redéfinir la méthode read() :

  • utiliser super.read() pour obtenir la valeur avant transformation
  • appliquer le filtre
  • retourner la valeur modifiée

 

Exemple


import java.io.*;

class DoubleReader extends FilterReader {

    private int lastChar = -1;
    public DoubleReader(Reader r) {
        super(r);
    }
    @Override
    public int read() throws IOException {
        if(lastChar != -1) {
            int r = lastChar;
            lastChar = -1;
            return r;
        }
        lastChar = super.read();
        return lastChar;
    }
}

                       

Nouvelles API java.nio.file

java.nio.file API

Java 8 introduit de nouvelles API pour la lecture de fichiers

Files est une classe contenant des méthodes statiques pour manipuler les fichiers et les dossiers.

Fonction pour la lecture

 

  • Files.newBufferedReader(Path p ) : Obtenir un buffer reader du fichier localisé au chemin p
  • Files.lines(Path p) : Obtenir un Stream contenant les lignes du fichier localisé au chemin p