Writer et
OutputStream, que nous venons de voir dans la section précédente, sont donc associées les classes
Reader et
InputStream respectivement.
Reader est une classe abstraite, étendue par
CharArrayReader,
StringReader et
FileReader, indirectement. Ces trois classes permettent de lire des flux de caractères en provenance de tableaux de
char, de chaînes de caractères, ou de fichiers.
La classe
Reader expose plusieurs versions d'une méthode
read(), dont le fonctionnement est constant. Cette méthode permet de lire des caractères, un par un, ou en les stockant dans un tableau. Toutes ces versions de
read() retournent un
int. Ce nombre correspond au nombre de caractères effectivement lus sur le flux. Si ce nombre est égal à -1, c'est que la fin du flux a été rencontrée. Voyons ceci sur un exemple.
Notons qu'un appel à
read() peut
bloquer
, c'est-à-dire, ne retourner de valeur qu'au bout d'un certain temps, durant lequel le flux est en attente de l'arrivée de nouveaux caractères.
Notons enfin qu'il peut arriver que le nombre de caractères lus soit plus petit que la taille du buffer, sans pour autant que la fin du flux soit atteinte.
Exemple 121. Utilisation de
FileReader
public static void main(String[] args) {
// déclaration de notre FileReader à l'exétieur des
// blocs try {} catch {}
FileReader fr = null ;
try {
// ouverture du flux de lecture
// peut jeter une FileNotFoundException
fr = new FileReader("tmp/bonjour.text") ;
// définition du buffer : un tableau de char
int bufferSize = 1024 ;
char [] buffer = new char[bufferSize] ;
// définitions de variables pour suivre notre
// lecture
int n = 0 ;
int total = 0 ;
int loops = 0 ;
do {
// remplissage du buffer
// n contient le nombre de caractères effectivement lus
n = fr.read(buffer) ;
total += n ;
loops++ ;
// si le nombre lu est -1,
// c'est que l'on a atteint la fin du flux
} while (n != -1) ;
// quelques informations sur la lecture
System.out.println(
"Nombre de caractères lus au total = " + total +
" en " + loops + " boucles.") ;
} catch (FileNotFoundException e) {
// gestion de l'erreur
} catch (IOException e) {
// gestion de l'erreur
} finally {
// pattern de fermeture d'un flux
if (fr != null) {
try {
fr.close() ;
} catch (IOException e) {
// gestion de l'erreur
}
}
}
}
FileReader expose également deux méthodes utilitaires.
skip(long n) : permet de sauter la lecture du nombre de caractères passés en paramètre. Cette méthode retourne le nombre de caractères effectivement sautés.
ready() : retourne
true si le flux possède des caractères prêts à être lus. Un appel à
read() après un appel à
ready() qui a retourné
true ne bloquera donc pas.
BufferedReader. En plus de sa fonctionnalité de bufferisation, elle expose une méthode très utile :
readLine(), qui retourne une chaîne de caractères (
String) contenant la ligne lue.
LineNumberReader(), qui permet de récupérer le numéro de ligne. Voyons ceci sur un exemple.
Exemple 122. Lecture d'un fichier ligne par ligne :
LineNumberReader
// le début du code est inchangé fr = new FileReader("tmp/bonjour.text") ; // ouverture d'un flux sur le flux de lecture du fichier LineNumberReader lnr = new LineNumberReader(fr) ; String line = null ; do { // lecture du numéro de la ligne courante int number = lnr.getLineNumber() ; // lecture d'une ligne line = lnr.readLine() ; // affichage de cette ligne, et de son numéro if (line != null) { System.out.println("[" + number + "] : " + line) ; } } while (line != null) ; // la fin du code est inchangée
OutputStream est la classe
InputStream. Cette classe abstraite est la base des classes de lecture des flux d'octets.
Elle est étendue par
FileInputStream pour la lecture dans des fichiers ;
ByteArrayInputStream pour la lecture dans des tableaux d'octets ;
DataInputStream pour la lecture des types primitifs Java ;
ObjectInputStream pour la lecture des objets sérialisés.
read() retourne le nombre d'octets effectifs qui ont été lus. Lorsque ce nombre est -1, la fin du flux a été atteinte.
La classe
InputStream expose les méthodes suivantes.
read(),
read(byte[] buf) et
read(byte[] buf, int offset, int length) : ces méthodes permettent de lire les octets dans un tableau. Elles retournent toutes le nombre d'octets effectivement lus.
skip(long n) : saute le nombre d'octets passé en paramètre dans le flux de lecture.
reset() : réinitialise la lecture de ce flux.
available() : retourne le nombre d'octets disponibles dans un
int. Ce nombre d'octets peut être lu, ou sauté. La lecture qui suit ne bloquera pas, tant que ce nombre ne sera pas dépassé.
Exemple 123. Utilisation de
FileInputStream
public static void main(String[] args) {
// déclaration de notre InputStream à l'exétieur des
// blocs try {} catch {}
InputStream is = null ;
try {
// ouverture du flux d'entrée sur un fichier
// peut jeter une FileNotFoundException
is = new FileInputStream(fichier) ;
// définition du buffer : un tableau d'octets
int bufferSize = 1024 ;
byte [] buffer = new byte[bufferSize] ;
// définitions de variables pour suivre notre
// lecture
int n = 0 ;
int total = 0 ;
int loops = 0 ;
do {
// remplissage du buffer
// n contient le nombre d'octets effectivement lus
n = is.read(buffer) ;
total += n ;
loops++ ;
// si le nombre lu est -1,
// c'est que l'on a atteint la fin du flux
} while (n != -1) ;
// quelques informations sur la lecture
System.out.println(
"Nombre d'octets lus au total = " + total +
" en " + loops + " boucles.") ;
} catch (FileNotFoundException e) {
// gestion de l'erreur
} catch (IOException e) {
// gestion de l'erreur
} finally {
// pattern de fermeture d'un flux
if (fr != null) {
try {
fr.close() ;
} catch (IOException e) {
// gestion de l'erreur
}
}
}
}
DataInputStream expose un jeu de méthodes qui permet de lire les types primitifs Java directement dans des flux binaires :
readInt(),
readLong(), etc...
Cette classe expose de plus une méthode
readFully(byte[] buf), implémentation de la même méthode de l'interface
DataInput. Le contrat de cette méthode est différent de celui de la méthode
read(byte[] buff) exposée entre autres par la classe
InputStream. La différence entre les deux est subtile, et mérite d'être expliquée ici.
La méthode
read(byte [] buff) lit un certain nombre d'octets, et les range dans le tableau passé en paramètre. Lorsqu'elle décide de rendre la main, le nombre d'octets effectivement écrit est retourné par cette méthode. Rien n'impose que ce nombre soit égal à la taille du tableau, il arrive souvent que ce ne soit pas le cas.
La méthode
readFully(byte[] buff), elle, garantit que le tableau retourné est bien rempli. Notons que cette méthode ne retourne rien. Si la fin du flux est rencontrée durant la lecture, alors une exception de type
EOFException est jetée, et le tableau passé en paramètre est partiellement rempli, sans que l'on puisse savoir de combien de cases. Cette méthode doit donc être utilisée avec précautions.
String
StringBuffer et
StringBuilder