String
;
write
de l'objet
ZipOutputStream
, les données écrites dans le fichier seront compressées de façon transparente pour l'utilisateur.
Writer
. Cette classe est abstraite, et expose les méthodes suivantes.
write(char[] buffer)
,
write(char[] buffer, int offset, int length)
,
write(String s)
,
write(String s, int offset, int length)
et
write(int c)
. Toutes ces méthodes permettent d'écrire des caractères en provenance de différentes sources (tableau ou chaîne de caractères). La dernière écrit un unique caractère stocké dans les 16 bits de poids faible de l'entier passé en paramètre.
append(char c)
,
append(CharSequence seq)
et
append(CharSequence seq, int start, int end)
. Ces méthodes fonctionnent de façon analogue aux précédentes. La différence est qu'elles retournent cette instance de
Writer
, ce qui permet de chaîner ces appels, comme lorsque l'on écrit dans un
StringBuffer
.
flush()
: permet de vider les buffers d'écriture vers le médium de sortie.
close()
: ferme ce flux.
flush()
et
close()
sont en général présentes dans toutes les API de ce type, et ont toujours la même sémantique. Notons que ces méthodes sont abstraites dans la classe
Writer
. Le fonctionnement exact n'est donc pas précisé au niveau de
>Write
. En particulier, l'appel à la méthode
flush()
avant la fermeture du flux n'est pas imposé.
Cette classe
Writer
est abstraite, on ne peut donc pas l'instancier directement. Elle est étendue par une série de classes, qui correspondent à chacun des média de sortie dont nous avons donné la liste en introduction :
FileWriter
,
StringWriter
et
CharArrayWriter
sont les principales.
Chacune des classes concrètes qui étendent
Writer
exposent leurs propres constructeurs.
FileWriter
se construit sur un fichier (instance de
File
). Pour
StringWriter
et
CharArrayWriter
on indique juste la taille de la chaîne ou du tableau dans lequel l'écriture va se faire.
Notons enfin que toutes ces méthodes jettent l'exception
IOException
, qu'il faut donc gérer. Voyons un premier exemple d'écriture dans un fichier.
Exemple 115. Utilisation de
FileWriter
, première version
public static void main(String[] args) { // définition d'un fichier File fichier = new File("tmp/bonjour.text") ; try { // ouverture d'un flux de sortie sur un fichier // a pour effet de créer le fichier Writer writer = new FileWriter(fichier) ; // écriture dans le fichier writer.write("Bonjour le monde !") ; // la méthode close de FileWriter appelle elle-même flush() writer.close() ; } catch (IOException e) { // affichage du message d'erreur et de la pile d'appel System.out.println("Erreur " + e.getMessage()) ; e.printStackTrace() ; } }
tmp
avant de la lancer.
Cela dit, ce n'est pas comme cela que l'on doit écrire ce code, notamment dans les applications serveur, susceptibles de fonctionner sans interruption pendant des mois.
Le problème vient de ce que la méthode
close()
n'est appelée que si aucune erreur n'est rencontrée lors de l'exécution du code. Par exemple, si l'écriture ne peut se faire, et jette une exception, alors l'exécution du code passe dans le bloc
catch
. Dans ce cas, le fichier qui a été ouvert n'est pas fermé. Bien que cela ne pose pas de problème dans le cas de ce code, puisque la machine Java s'éteint immédiatement, il faut tout de même corriger cette erreur tout de suite, pour ne pas prendre de mauvaises habitudes !
Exemple 116. Utilisation de
FileWriter
, , version corrigée
public static void main(String[] args) { // définition d'un fichier File fichier = new File("tmp/bonjour.text") ; // la définition du writer doit se faire ici // pour des raisons de visibilité Writer writer = null ; try { // ouverture d'un flux de sortie sur un fichier // a pour effet de créer le fichier writer = new FileWriter(fichier) ; // écriture dans le fichier writer.write("Bonjour le monde !") ; } catch (IOException e) { // affichage du message d'erreur et de la pile d'appel System.out.println("Erreur " + e.getMessage()) ; e.printStackTrace() ; } finally { // il se peut que l'ouverture du flux ait échoué, // et que ce writer n'ait pas été initialisé if (writer != null) { try { // la méthode close de FileWriter appelle elle-même flush() writer.close() ; } catch (IOException e) { System.out.println("Erreur " + e.getMessage()) ; e.printStackTrace() ; } } } }
flush()
.
L'API Java IO offre le support de la bufferisation par la classe
BufferedWriter
. Cette classe étend la classe
Writer
, et son constructeur prend en paramètre un flux de type
Writer
. L'utilisation de cette classe dans notre exemple précédent donne ceci.
Exemple 117. Utilisation d'un buffer d'écriture
// le début du code est inchangé // ouverture d'un flux de sortie sur un fichier // a pour effet de créer le fichier writer = new FileWriter(fichier) ; Writer bufferedWriter = new BufferedWriter(writer) ; // écriture dans le fichier bufferedWriter.write("Bonjour le monde !") ; // la fin du code est inchangée
flush()
d'un flux bufferisé est important, car cela permet de vider les buffers vers le medium de sortie, disque ou réseau.
Cette instance de
BufferedWriter
ne crée pas à proprement parler de nouveau flux. Plutôt, elle enveloppe le flux existant sur le fichier
fichier
, et ajoute au flux existant les fonctionnalités de bufferisation. Fermer le médium de sortie via l'instance
bufferedWriter
ou
writer
donne le même résultat, puisqu'il n'y a qu'une unique ouverture de fichier.
PrintWriter
étend la classe
Writer
. Elle expose de nombreux constructeurs, qui permettent de créer une instance de
PrintWriter
sur de nombreux types de flux, dont une instance de
Writer
. Cette classe fonctionne donc aussi comme une enveloppe sur le flux passé en paramètre.
Elle expose des méthodes bien connues en C :
print(...)
,
println(...)
et
printf(...)
. Ces méthodes peuvent prendre des formats en paramètres, ainsi que des locales. Elles permettent donc de formatter des affichages, en prenant en compte les conventions linguistiques de formatage, lorsque la locale est passée en paramètre.
Notons que pour l'écriture dans des fichiers, la classe
PrintWriter
expose également un constructeur qui prend une instance de
File
. En l'utilisant on n'a donc pas besoin de construire soi-même l'instance de
Writer
.
Voyons un exemple d'utilisation d'un
PrintWriter
.
Exemple 118. Utilisation de
PrintWriter
File fichier = new File("tmp/bonjour.text") ; Writer writer = null ; try { // ouverture d'un flux de sortie sur un fichier writer = new FileWriter(fichier) ; // création d'un PrintWriter sur ce flux PrintWriter pw = new PrintWriter(writer) ; // écriture d'un marin dans le fichier Marin m = new Marin("Surcouf", "Robert") ; // la méthode toString() est appelée pw.println(m) ; } catch (IOException e) { // gestion des erreurs } finally { // fermeture du flux }
OutputStream
, qui joue un rôle équivalent à celui joué par la classe
Writer
. Cette classe est abstraite, et étendue par les classes
FileOutputStream
pour l'écriture dans des fichiers, et
ByteArrayOutputStream
pour l'écriture dans des tableaux d'octets.
La classe
OutputStream
expose les méthodes suivantes.
write(byte [] b)
,
write(byte [] b, int offset, int length)
et
write(int b)
. Ces méthodes permettent d'écrire des octets directement sur le flux. Dans le cas de l'écriture d'un entier, seuls les 8 bits de poids faibles sont pris en compte.
close()
et
flush()
qui ont la même sémantique que celles de la classe
Writer
.
FileOutputStream
. Le pattern suivi est identique à celui de
FileWriter
.
Exemple 119. Utilisation de
FileOutputStream
public static void main(String[] args) { // définition d'un fichier File fichier = new File("tmp/array.bin") ; OutputStream os = null ; // création d'un tableau d'octets, qui sera // écrit dans le fichier byte [] byteArray = { 0, 1, 2 } ; try { // ouverture d'un flux de sortie sur un fichier os = new FileOutputStream(fichier) ; // écriture proprement dite os.write(byteArray) ; } catch (IOException e) { System.out.println("Erreur " + e.getMessage()) ; e.printStackTrace() ; } finally { // fermeture du fichier dans le bloc finally if (os != null) { try { // la méthode close de FileOutputStream appelle elle-même flush() os.close() ; } catch (IOException e) { System.out.println("Erreur " + e.getMessage()) ; e.printStackTrace() ; } } }
ByteArrayOutputStream
permet de diriger le flux de sortie vers un tableau d'octets, qui sert alors de
buffer
. Cette classe expose quelques méthodes supplémentaires pour la gestion de ce buffer.
reset()
: permet de remettre à zéro le pointeur d'écriture de ce buffer, ce qui a pour effet d'annuler tout ce qui a été écrit.
size()
: retourne la taille du buffer. Notons que la taille de ce buffer peut être fixée à la construction de cette instance de
ByteArrayOutputStream
.
toByteArray()
: retourne le tableau d'octets sur lequel ce buffer est construit.
toString(String charsetName)
: permet de convertir le contenu de ce tableau d'octets en utilisant le jeu de caractères précisé en argument. La liste des jeux de caractères supportés est donnée dans la javadoc de la classe
Charset
.
DataOutputStream
, extension de
FilterOutputStream
, elle-même extension de
OutputStream
. Cette classe expose une méthode par type primitif, qui permet décrire ce type sur un flux d'octets. La construction d'une instance de
DataOutputStream
prend en paramètre une instance de
OutputStream
, de façon classique. Voyons ceci sur un exemple.
Exemple 120. Utilisation de
DataOutputStream
public static void main(String[] args) { File fichier = new File("tmp/integers.bin") ; OutputStream os = null ; try { // ouverture d'un flux de sortie sur un fichier os = new FileOutputStream(fichier) ; // ouverture d'un flux de type DataOutputStream // sur ce même fichier DataOutputStream dos = new DataOutputStream(os) ; for (int i : Arrays.asList(1, 2, 3, 4, 5)) { dos.writeInt(i) ; } } catch (IOException e) { // gestion de l'erreur } finally { // fermeture du flux } } }
writeInt(int)
,
writeLong(long)
, etc...
ObjectOutputStream
supporte l'écriture directe d'objets sur des flux. Ce mécanisme particulier s'appelle
serialization
, et a un fonctionnement particulier. Il permet de garantir, entre autres, qu'une instance d'une classe écrite dans un fichier est bien recréée dans la bonne classe, identique à la première. Ce mécanisme peut aussi être surchargé de différentes façons.
Nous verrons ces techniques dans une section particulière, en fin de ce chapitre.
String
StringBuffer
et
StringBuilder