javaio.png

Les flux Java se décomposent en deux grandes catégories,

  • les flux binaires
  • les flux textes

Un flux binaire (byte stream) permet de considérer un flux (un fichier, un socket, ...) comme un ensemble de bytes. Lorsque j'écris dans un flux, j'écris un byte

Programs use byte streams to perform input and output of 8-bit bytes. All byte stream classes are descended from InputStream and OutputStream.

Extrait de Oracle ex Sun

Un flux texte (character stream) permet de considérer un flux comme un ensemble de caractères. Lorsque j'écris dans un flux, j'écris un caractère.

The Java platform stores character values using Unicode conventions. Character stream I/O automatically translates this internal format to and from the local character set. In Western locales, the local character set is usually an 8-bit superset of ASCII.

Extrait de Oracle ex Sun

Hmm Là, ce n'est plus si simple. Puisque l'on me parle d'un superensemble de l'ASCII, je dois penser à ma locale (chez moi c'est fr_BE-UTF8). Je dois également avoir en tête que

  • un caractère en Java est codé en Unicode, sur 2 bytes[1].
  • Java va devoir faire un appel système a un moment donné afin d'écrire le caractère dans le flux (et là ma locale va influencer le tout)

... il est temps de faire quelques tests. Et (naturellement) le plus simple est d'utiliser les fichiers comme flux. Je vais donc écrire dans un fichier. J'écris un petit programme tout simple du style;

import java.io.OutputStream; 
import java.io.FileOutputStream; 
import java.io.IOException;
 
public class SeizeOutputStream { 
	 public static void main ( String[] args ) { 
		  OutputStream os; 
		  try {
			  os = new FileOutputStream("file");
			  os.write(16);
			  os.close(); 
		  } catch (IOException e) {
			  System.err.println("Erreur: " 
                                + e.getMessage()); 
			  System.exit(1);
		  }
	 }
}

Si je visualise le fichier, je ne vois rien de probant;un caractère "illisible". Un petit détour par la table unicode correspondant (pdf) m'informe que le caractère de code unicode 16 (0x10) est le caractère DLE inreprésentable.

0010 <control> = DATA LINK ESCAPE

Si je fait un hexdump, je retrouve mon 16 (qui, pour rappel, vaut bien 0x10 en hexa).

$ hexdump  file -C 
00000000  10                                                |.|
00000001

... et si je lis le fichier avec un FileInputStream ?

import java.io.InputStream; 
import java.io.FileInputStream;
import java.io.IOException;
 
public class SeizeInputStream { 
	 public static void main ( String[] args ) { 
		  InputStream r;
		  try {
			  r = new FileInputStream("file");
			  System.out.println("Char: " + r.read());
			  r.close();
		  } catch (IOException e) {
			  System.err.println("Erreur: " 
                            + e.getMessage());
			  System.exit(1);
		  }
	 }
}

Je retrouve mon 16.

$ java SeizeInputStream 
Char: 16

Faisons exactement la même chose avec les flux texte. J'écris le code

import java.io.Writer; 
import java.io.FileWriter; 
import java.io.IOException;
 
public class SeizeWriter { 
	 public static void main ( String[] args ) { 
		  Writer os; 
		  try {
			  os = new FileWriter("file");
			  os.write(16);
			  os.close(); 
		  } catch (IOException e) {
			  System.err.println("Erreur: " 
                            + e.getMessage()); 
			  System.exit(1);
		  }
	 }
}

Je regarde le contenu de mon fichier, toujours ce même caractère et le "hexdump" me donne aussi le même résultat. Quelle est donc la différence entre les deux ? Ais-je bien compris [2] ce que j'écris dans le fichier ?

Dans le cas du flux binaire, j'écris un byte qui est le byte de poid faible de l'int que je passe en paramètre à la méthode write de la classe OutputSream. Dans le cas du flux texte, j'écris deux bytes qui sont les deux bytes de poid faible de l'int que je passe en paramètre à la méthode write de la classe Writer.

... Évidemment en choisissant 16 comme valeur, ce n'est pas probant. Remplacons la ligne

os.write(16) 

par (je pense que l'on part en Asie)

os.write(0x4E07)

... et l'on peut maintenant voir une différence.

writer.png

outputstream.png

Comment interpréter cette différence ?

Le caractère &#x4E07; [3] est bien le caractère de code unicode 0x4E07 et c'est normal que ce soit lui que je vois apparaitre dans un contexte "texte".

Le caractère 'N' que je vois dans le contexte binaire provient du fait que j'écris le byte de poid faible contenu dans l'int 0x4E07 ... qui est la valeur 0x4E ... qui est le code unicode du caractère 'N'. Et c'est gagné ! (Les attentifs se demanderont si l'on est petit indien ou grand indien avant de poser la question: "Et pourquoi pas le caractère de controle 'BEL' de code unicode 0x0007 ?).

En espérant que ceci vous a apporté un petit éclairage ...

Références

À lire aussi

Notes

[1] Tous les mots ont de l'importance. Je parle d'un caractère, pas d'une chaîne. J'induis le fait que Java code en UTF16 et donc qu'il ne permet qu'un sous-ensemble d'Unicode. J'ai déjà dis tout ça dans ce post

[2] C'est une figure de style. Maintenant que j'écris ce post, c'est que c'est plus ou moins clair dans ma tête ^^

[3] Il est possible que votre navigateur ne le représente pas. Ça dépend du navigateur et de la police utilisée.