5. Lecture et écriture de flux croisés

5.1. Introduction

L'API Java I/O permet de lire et écrire en mode caractères des flux binaires. Les deux classes utilisées pour ce faire sont des extensions de Reader et Writer : InputStreamReader et OutputStreamWriter respectivement.

5.2. Lire des caractères sur un flux binaire : classe InputStreamReader

Comme chacun sait, un fichier informatique ne contient que des octets, mis les uns à la suite des autres. Aussi bête qu'une rangée de balais dans un placard, ce tas d'octets n'a de sens que parce que l'on veut bien lui en donner ! Un ou plusieurs octets qui se suivent ne peuvent constituer un caractère, et par la suite une chaîne de caractères, que si l'on précise un encodage , table de conversion entre les valeurs numériques de ces octets (ou séquence d'octets) et des caractères. Cet encodage est purement conventionnel, et susceptible d'évoluer au cours du temps. À l'origine des temps informatiques, l'encodage le plus fréquemment utilisé était le code ASCII, toujours utilisé aujourd'hui, même si ses formes ont évolué. Aujourd'hui, l'encodage qui tient la corde est bien sûr l'Unicode. Plus de la moitié des textes échangés sur l'Internet actuel utilisent cet encodage. Pour pouvoir lire des caractères dans un flux binaire, une instance de InputStreamReader a donc besoin de connaître l'encodage de ces caractères. Les méthodes de lecture exposées par InputStreamReader sont imposées par la classe Reader qu'elle étend, et il n'est pas possible de leur passer cette information. C'est donc à la construction de cet objet qu'il faut préciser cet encodage. Cet encodage peut être précisé de trois manières.
  • Ce peut être une instance de CharSet, classe du package java.nio, qui modélise un encodage particulier. Une instance de CharSet se construit en passant le nom de l'encodage choisi sous forme d'une chaîne de caractères. Les chaînes standard, que toute JVM doit supporter, sont donnés dans la suite de cette partie.
  • Ce peut être une instance de CharSetDecoder, classe abstraite du package java.nio. Cet objet est capable de décoder un flux d'octets en flux de caractères.
  • Ce peut être enfin, et c'est le constructeur le plus simple à utiliser, une chaîne de caractères, qui indique le nom du jeu de caractères à utiliser.
Toute JVM doit connaître les jeux de caractères suivants, dont les noms qui suivent peuvent être utilisés pour construire des instances de CharSet ou passés en paramètre du constructeur de InputStreamReader.
  • US-ASCII : ASCII codé sur 7 bits ;
  • ISO-8859-1 : aussi appelé ISO-LATIN-1 ;
  • UTF-8 : utilisé pour l'Unicode ;
  • UTF-16BE : encodage 16 bits, octet de poids fort en premier (BE = big endian ) ;
  • UTF-16LE : encodage 16 bits, octet de poids faible en premier (LE = little endian ) ;
  • UTF-16 : encodage 16 bits, l'ordre des octets est identifié par un octet optionnel, appelé BOM ( byte order marker ), qui figure en premier dans le flux binaire.
Notons enfin que la classe InputStreamReader expose une méthode getEncoding() qui permet d'obtenir le nom du jeu de caractères utilisé.

5.3. Écrire des caractères sur un flux binaire : classe OutputStreamWriter

La classe OutputStreamWriter est rigoureusement symétrique de InputStreamReader. On la construit en lui passant en paramètre une indication sur le jeu de caractères qu'elle doit utiliser. Elle expose les méthodes d'écriture de la classe Writer, qu'elle étend.

5.4. Remarque sur les jeux de caractères

La machine Java utilise un jeu de caractères par défaut pour son fonctionnement interne. Ce jeu de caractères est précisé par la propriété système file.encoding. On peut obtenir la valeur de cette propriété par la méthode System.getProperty("file.encoding").

Exemple 124. Propriété file.encoding

// dans une méthode main
 // affichage de l'encodage courant des fichiers
System.out.println(System.getProperty("file.encoding")) ;

 // changement de la valeur de cette propriété
System.setProperty("file.encoding",  "UTF-8") ;
 // affichage du nouvel encodage
System.out.println(System.getProperty("file.encoding")) ;

Sur une machine Windows, il se peut que l'encodage soit Cp1252. Le positionner à une valeur plus civilisée comme UTF-8 est probablement une bonne idée !
Java langage & API
Retour au blog Java le soir
Cours & Tutoriaux
Table des matières
Introduction : un peu d'histoire
1. Java : genèse d'un nouveau langage
Programmer en Java
1. Un premier exemple
1.1. Aperçu général, cycle de vie
1.2. Un premier programme
1.3. Programmes, applets, servlets, etc...
2. Une première classe
2.1. Écriture d'une classe
2.2. Instanciation de cette classe
3. Types de base, classes et objets
3.1. Types de base
3.2. Classes et objets
Classes Object et String
1. Introduction
2. La classe Object
2.1. La méthode toString()
2.2. La méthode clone()
2.3. La méthode equals()
2.4. La méthode hashCode()
2.5. La méthode finalize()
2.6. La méthode getClass()
3. La classe String
3.1. Introduction
3.2. Construction d'un objet de type String
3.3. Concaténation, StringBuffer et StringBuilder
3.4. Concaténations de chaînes de caractères depuis Java 8
3.5. Concaténations de chaînes de caractères depuis Java 11
3.6. Extraction d'une sous-chaîne de caractères
3.7. Comparaison de deux chaînes de caractères
3.8. Méthodes de comparaisons lexicographiques
3.9. Méthode de recherche de caractères
3.10. Méthode de modification de chaîne
3.11. Méthode de duplication
3.12. Support de l'unicode, internationalisation
Structure d'une classe
1. Introduction
2. Classes
2.1. Classes publiques
2.2. Classes internes
2.3. Classe membre
2.4. Classes locales
2.5. Classes anonymes
2.6. Le mot-clé this
3. Éléments statiques
3.1. Champ statique
3.2. Cas des constantes
3.3. Bloc statique
3.4. Classe membre statique
4. Membres d'une classe, visibilité
4.1. Bloc non statique
4.2. Accès à un membre, visibilité
4.3. Les champs
4.4. Signature d'une méthode
4.5. Les méthodes
4.6. Getters et Setters
5. Constructeur, instanciation
5.1. Chargement d'une classe
5.2. Constructeurs d'une classe
5.3. Instanciation d'un objet
5.4. Destruction d'objets
5.5. Le mot-clé final
6. Énumérations
6.1. Déclaration d'une énumération
6.2. Classe énumération
6.3. Méthode toString()
6.4. Méthode valueOf()
6.5. Méthode values()
6.6. Méthode ordinal()
6.7. Méthode compareTo()
6.8. Constructeurs privés
6.9. Classe utilitaire : EnumSet
Noms, opérateurs, tableaux
1. Introduction
2. Identificateurs, noms et expressions
2.1. Identificateurs et noms
2.2. Expressions
3. Opérateurs, ordre d'exécution
3.1. Ordre d'exécution
3.2. Les opérateurs
3.3. Les opérateurs ++ et --
3.4. Les opérateurs % et /
3.5. Les opérateurs <<, >> et >>>
3.6. L'opérateur instanceof
3.7. Les opérateurs &, | et ^
3.8. Les opérateurs && et ||
3.9. L'opérateur ? ... :
3.10. Les opérateurs d'affectation
4. Tableaux
4.1. Création d'un tableau
4.2. Initialisation d'un tableau
4.3. Utilisation d'un tableau comme un Object
4.4. Tableaux de tableaux
4.5. Copie de tableaux
5. Blocs, boucles et contrôles
5.1. Blocs
5.2. Mots-clés réservés
5.3. Tests : if et switch
5.4. Boucles : for, while, do ... while
5.5. Commandes continue et break
5.6. Commandes return et goto
Nombres, précision, calculs
1. Introduction
2. Calculs
2.1. Précision
2.2. Codage des nombres flottants
2.3. Le mot-clé strictfp
2.4. Conversion de types
3. Dépassements de capacité
3.1. Cas des entiers
3.2. Cas des flottants
3.3. Bibliothèques BigInteger et BigDecimal
4. Fonctions mathématiques
4.1. Fonctions usuelles
4.2. Générateurs aléatoires
5. Classes enveloppe
5.1. Associer les types de base à des objets
5.2. Auto-boxing
Héritage, abstraction, interfaces
1. Introduction
2. Abstraction et encapsulation
2.1. Abstraction
2.2. Encapsulation
3. Héritage
3.1. Définition de l'héritage
3.2. Conséquences pour les membres
3.3. Polymorphisme
3.4. Empêcher l'héritage
4. Classes abstraites
5. Interfaces
5.1. Introduction
5.2. Définition
5.3. Java 8 et les interfaces
5.4. Utilisation des interfaces
5.5. Définition de constantes dans les interfaces
5.6. Utilité des interfaces
Packages
1. Introduction
2. Notion de paquet
2.1. Déclaration d’appartenance à un paquet
2.2. Chargement d’une classe
2.3. Choix de nom
3. Archives, chemin de recherche, classpath
3.1. Archives
3.2. Variable CLASSPATH
3.3. Notion de classloader
3.4. Bilan sur les classes chargées
3.5. Visibilité
3.6. Conseils d'écriture, bibliothèque standard
Exceptions
1. Introduction
2. Erreurs et Exceptions
2.1. Classe Throwable, notion de stack trace
2.2. Classe Error
2.3. Classe RuntimException
2.4. Classe Exception
3. Déclenchement d'une exception
3.1. Exceptions déclenchées par la JVM
3.2. Exceptions déclenchées par l'application
4. Capter une exception
4.1. Traiter une exception localement
4.2. Code de captage
5. Créer ses propres exceptions
Entrées / sorties
1. Introduction
2. Notion de fichier
2.1. Introduction
2.2. La classe File
2.3. Construction d'une instance de File
2.4. Méthodes exposées
3. Flux de sortie
3.1. Introduction, notion de flux
3.2. Écriture de caractères, classe Writer
3.3. Bufferisation, construction d'un flux sur un autre
3.4. Utilisation de PrintWriter
3.5. Écriture d'octets, OuputStream
3.6. Écriture de types primitifs : DataOutputStream
3.7. Écriture d'objets : ObjectOutputStream
4. Flux d'entrée
4.1. Introduction
4.2. Lecture de caractères, classe Reader
4.3. Bufferisation, lecture ligne par ligne
4.4. Lecture d'octets : InputStream
4.5. Lecture de types primitifs : DataInputStream
4.6. Lecture d'objets : ObjectInputStream
5. Lecture et écriture de flux croisés
5.1. Introduction
5.2. Lire des caractères sur un flux binaire : classe InputStreamReader
5.3. Écrire des caractères sur un flux binaire : classe OutputStreamWriter
5.4. Remarque sur les jeux de caractères
6. Serialization d'objets
6.1. Enjeu de la sérialization d'objets
6.2. Serialization d'un objet
6.3. Sérialization d'une grappe d'objets
6.4. Première surcharge : méthode writeObject() readObject()
6.5. Deuxième surcharge : utilisation d'un externalizer
6.6. Troisième surcharge : utilisation d'un objet proxy
7. Flux compressés
7.1. Introduction
7.2. Flux de type gzip
7.3. Flux de type zip