Les flux de données
Déterminer la classe de flux à utiliser
Dans la hiérarchie des classes du paquetage java.io, il y a beaucoup de classes ! Ce qui évidemment peut en faire reculer plus d'un ! Que choisir ?
En fait, ce nombre de classes est logique et facilement compréhensible, donc pas de panique:
Tout d'abord, il faut distinguer les classes de flux sur caractères et les classes de flux binaires. Et pour ces flux, si vous voulez faire une lecture ou une écriture.
Nous avons donc:
Les classes de flux de type caractères dérivant de:
- java.io.Reader pour la lecture
- java.io.Writer pour l'écriture
Les classes java.io.Reader
et java.io.Writer étant des classes abstraires.
Les noms de ces classes se termineront ainsi par Reader ou Writer suivant le cas pour lire ou écrire des caractères.
Les classes de flux de type binaires dérivant de:
- java.io.InputStream pour la lecture
- java.io.OutputStream pour l'écriture
Les classes java.io.InputStream
et java.io.OutputStream étant elles aussi des classes abstraires.
Les noms de ces classes se termineront cette fois-ci par InputStream ou OutputStream suivant le cas.
Ce qu'il reste à définir, c'est la source (lecture) ou la destination (écriture) des différentes sources de données:
Les classes de flux de type caractère en lecture ou écriture seront préfixées:
- Pour un fichier par File
- Un tableau de caractères par CharArray
- Un pipeline par Pipe
- Une chaîne de caractères par String
Les classes de flux de type binaire en lecture ou écriture seront préfixées:
- Pour un fichier par File
- Un tableau d'octets par ByteArray
- Un pipeline par Pipe
Ainsi, pour lire des caractères provenant d'un fichier, nous utiliserons la classe FileReader.
Facile non ?
Ouverture d'un flux en lecture
Pour un flux de type caractère
Il est possible d'utiliser une instance de type File pour ouvrir un fichier:
File f = new File( "monFichierChar" );
FileReader fr = new FileReader( f );
ou encore directement
FileReader fr = new FileReader( new File( "monFichierChar" ));
Cependant, la classe FileReader propose aussi une solution pour ouvrir un fichier en utilisant un chemin/nom fichier:
FileReader fr = new FileReader( "monFichierChar" );
Pour un flux de type binaire
les solutions seront similaires que précédemment:
File f = new File( "monFichierBin" );
FileInputStream fis = new FileInputStream( f );
ou encore directement
FileInputStream fis = new FileInputStream( new File( "monFichierBin" ));
ou enfin:
FileInputStream fis = new FileInputStream( "monFichierBin" );
Quelques méthodes lors de la lecture d'un flux de données
Voici des méthodes pour lire des données d'un flux de type caractère ou de type binaire:
Désignation | Classe java.io.Reader | Classe java.io.InputStream | Remarque |
Lecture d'une donnée | public int read() | public int read() | Retourne la donnée sous forme d'entier, ou -1 si fin du fichier atteint. |
Lecture de la source vers le tableau buffer de length données maximum(voir méthode length qui indique la taille du tableau) | public int read (char[] buffer) | public int read (byte[] buffer) | La méthode retournera le nombre de caractères ou d'octets effectivement lu, ou -1 si lecture alors qu'il n'y avait pas de données car fin du fichier atteint. |
Lecture de la source vers le tableau buffer à partir d'un indice offset de ce tableau et pour une certaine taille length maximum. | public int read (char[] buffer, int offset, int length) | public int read (byte[] buffer, int offset, int length) | La méthode retournera le nombre de caractères ou d'octets effectivement lu, ou -1 si lecture alors qu'il n'y avait pas de données car fin du fichier atteint. |
Puis des méthodes de tous genres pour ces même classes
Désignation | Classe java.io.Reader | Classe java.io.InputStream | Remarque |
Indique si vrai que le flux supporte le marquage de positions. | boolean markSupported() | boolean markSupported() | Si vrai, il sera alors possible de se repositionner sur une position précédente dans le flux pour relecture. |
Permet de mémoriser la position courante | mark(int readlimit) | mark(int readlimit) | Non suportée pour par toutes les classes, il faudra donc le vérifier avant via markSupported ! En entrée readlimit : Indique le nombre maximum de caractères qui peuvent être lus et où la méthode reset pourra être utilisée. Si plus de readlimit données sont lus, la classe n'a plus obligation à retenir le marquage et le reset provequera une IOException. |
Retourner à la dernière position marquée | reset() | reset() | Non suportée pour par toutes les classes, il faudra donc le vérifier avant via markSupported ! |
Indique que le flux est prêt pour une lecture | boolean ready() | boolean ready() | |
Sauter un nombre de caractères ou d'octets | long skip(long) | long skip(long) | En entrée: long est le nombre de données à sauter. La valeur doit être positive (impossible de faire marche arrière !) Retourne le nombre de données effectivement sautés... |
Exemple de code de lecture d'un flux à partir d'un fichier:
Ce petit petit programme va lire un fichier texte en mode caractères donc.
Il saute volontairement les 10 premiers caractères puis affiche dans la console tout le restant du fichier.
A vous de remplacer monfichier.txt par un chemin et un nom de fichier corrects.
Pour rappel, si vous
utilisez un chemin à la sauce Windows, n'oubliez pas de doubler les \
car en JAVA, \ suivi d'un caractère permet d'utiliser des caractères
spéciaux...
On notera enfin que FileReader ne supporte pas le marquage.
On peux utiliser File pour ouvrir un fichier et le lire ensuite ...
Il suffit d'imbriquer...
Ouverture d'un flux en écriture
Pour un flux de type caractère
Là encore, très ressemblant à la création d'une instance en lecture...
Il est aussi possible d'utiliser une instance de type File pour ouvrir un fichier:
File f = new File( "monFichierChar" );
FileWriter fw = new FileWriter( f );
ou encore directement
FileWriter fw = new FileWriter( new File( "monFichierChar" ));
ou encore:
FileWriter fw = new FileWriter( "monFichierChar" );
Pour ouvrir un fichier en maj, il faudra utiliser d'autres méthodes forts similaires, puisqu'il s'agit d'ajouter un booléen qui précisera s'il faut ajouter à la fin (true) ou repartir d'une enveloppe vide du fichier (false) pour mettre les futurs données:
Avec boolean append à vrai ou faux:
File f = new File( "monFichierChar" );
FileWriter fw = new FileWriter( f, append);
ou encore directement
FileWriter fw = new FileWriter( new File( "monFichierChar" ), append);
ou encore:
FileWriter fw = new FileWriter( "monFichierChar", append );
Pour un flux de type binaire
les solutions seront similaires que précédemment:
File f = new File( "monFichierBin" );
FileOutputStream fos = new FileOutputStream( f );
ou encore directement
FileOutputStream fos = new FileOutputStream( new File( "monFichierBin" ));
ou enfin:
FileOutputStream fos = new FileOutputStream( "monFichierBin" );
En ajout éventuel, là aussi nous passerons pas un booléen:
Soit boolean append à vrai ou faux suivant le cas:
File f = new File( "monFichierBin" );
FileOutputStream fos = new FileOutputStream( f, append);
ou encore directement
FileOutputStream fos = new FileOutputStream( new File( "monFichierBin" ), append);
ou enfin:
FileOutputStream fos = new FileOutputStream( "monFichierBin", append);
Quelques méthodes pour l'écriture d'un flux de données
Voici des méthodes pour écrire des données d'un flux de type caractère ou de type binaire:
Désignation | Classe java.io.Writer | Classe java.io.OutputStream | Remarque |
Ecriture d'une donnée | public void write(int) | public write(int) | Soit un caractère, soit un byte. |
Ecriture d'une chaine de caractères | public void write(String) | ||
Ecriture d'une partie de chaine de caractères à partir d'une position et sur un nombre de caractères. | public void write(String, int offset, int length) | ||
Ecriture d'un tableau buffer de length données maximum(voir méthode length qui indique la taille du tableau) | public void write (char[] buffer) |
public void write(byte[] buffer) | |
Ecriture d'un tableau buffer à partir d'un indice offset de ce tableau et pour une certaine taille length maximum. | public void write(char[] buffer, int offset, int length) | public void write (byte[] buffer, int offset, int length) |
Exemple de code d'écriture d'un flux sur un fichier:
Gestion des erreurs
Evidemment, bien que non précisé dans mes tableaux, toutes ces gestions de flux peuvent provoquer des erreurs. Cela impliquera la mise en place de gestions des exceptions...
Fermer un flux en écriture/lecture
Comme dans tous les langages, si vous ouvrez un flux, il faudra le fermer.
Désignation | Classe java.io.Reader | Classe java.io.InputStream | Remarque |
Fermeture du flux de données | public void close() | public void close() | A mettre dans tous les cas pour terminer avec le flux.Indispensable lors d'une maj sinon, le fichier sera vide ! |
On préfèrera
utiliser la méthode close() dans un bloc finally, permettant ainsi une
fermeture propre du flux, qu'il y ait eu ou non une exception. Dans le même
principe, la déclaration de la variable permettant d'identifier le flux
(monFluxFichier dans mon exemple) sera à déclarer hors du bloc
try et cette variable sera positionnée à null. Cela permet de
tester cette variable avant de faire un close(). Si la variable est toujours
à null lors du finally, c'est que le flux n'a pas été ouvert,
donc rien à faire (voir exemples ci-dessus) !
Quelques remarques sur FileReader/FileWriter
JAVA utilise les caractères Unicode. Donc Filreader pourra lire des flux caractères Unicode ou ASCII (puisque la table ASCII fait partie intégrante de la table UNICODE).
Il est évidemment possible de travailler avec d'autres tables de codage. Cette tâche est dévolue à la classe mère de FileReader/FileWriter qui n'est autre que la classe OutputStreamReader ou InputStreamWriter. Au niveau du constructeur, vous pourrez indiquer la table de codage...
Quelques remarques tout court...
Evidemment, les solutions présentaient ici semblent venir d'une époque bien lointaine, car de nos jours, il existe d'autres outils de développement nous permettant de gérer les flux de manière bien plus sympathique. Par exemple pouvoir extraire une ligne complète d'un fichier.
En fait, c'est aussi le cas en JAVA. Nous pourrons utiliser des filtres que nous allons appliquer à nos flux...
Autres liens
Cf. la classe File pour gérer des fichiers/répertoires
Cf. la classe RandomAccessFile pour gérer des fichiers à accès direct.