Un regard approfondi sur le type de caractère de Java

La version 1.1 de Java introduit un certain nombre de classes pour traiter les caractères. Ces nouvelles classes créent une abstraction pour la conversion d'une notion spécifique à la plate-forme de valeurs de caractère en valeurs Unicode . Cette colonne examine ce qui a été ajouté et les motivations pour ajouter ces classes de caractères.

Tapez char

Le type de base le plus utilisé dans le langage C est peut-être le type char . Le type char est abusé en partie parce qu'il est défini comme étant 8 bits, et pour les 25 dernières années, 8 bits a également défini le plus petit bloc de mémoire indivisible sur les ordinateurs. Lorsque vous combinez ce dernier fait avec le fait que le jeu de caractères ASCII a été défini pour tenir sur 7 bits, le type char fait un type "universel" très pratique. De plus, en C, un pointeur vers une variable de type char est devenu le type de pointeur universel parce que tout ce qui pouvait être référencé en tant que char pouvait également être référencé comme n'importe quel autre type via l'utilisation de la conversion.

L'utilisation et l'abus du type char dans le langage C ont conduit à de nombreuses incompatibilités entre les implémentations du compilateur, donc dans la norme ANSI pour C, deux modifications spécifiques ont été apportées: Le pointeur universel a été redéfini pour avoir un type de void, nécessitant ainsi un déclaration du programmeur; et la valeur numérique des caractères était considérée comme signée, définissant ainsi la manière dont ils seraient traités lorsqu'ils seraient utilisés dans les calculs numériques. Puis, au milieu des années 1980, les ingénieurs et les utilisateurs ont compris que 8 bits étaient insuffisants pour représenter tous les personnages du monde. Malheureusement, à ce moment-là, C était si ancré que les gens n'étaient pas disposés, peut-être même incapables, de changer la définition du chartype. Passez maintenant aux années 90, aux débuts de Java. L'un des nombreux principes énoncés dans la conception du langage Java était que les caractères seraient de 16 bits. Ce choix prend en charge l'utilisation d' Unicode , une manière standard de représenter de nombreux types de caractères dans de nombreuses langues différentes. Malheureusement, cela a également ouvert la voie à une variété de problèmes qui ne sont que maintenant en cours de résolution.

Qu'est-ce qu'un personnage de toute façon?

Je savais que j'avais des ennuis quand je me suis retrouvé à me poser la question «Alors qu'est - ce qu'un personnage? Eh bien, un personnage est une lettre, non? Un tas de lettres composent un mot, des mots forment des phrases, etc. La réalité, cependant, est que la relation entre la représentation d'un caractère sur un écran d'ordinateur, appelé son glyphe , et la valeur numérique qui spécifie que le glyphe, appelé a code point, n'est pas du tout simple.

Je me considère chanceux d'être un locuteur natif de la langue anglaise. Premièrement, parce que c'était le langage commun d'un nombre important de ceux qui ont contribué à la conception et au développement de l'ordinateur numérique moderne; deuxièmement, parce qu'il a un nombre relativement petit de glyphes. Il y a 96 caractères imprimables dans la définition ASCII qui peuvent être utilisés pour écrire l'anglais. Comparez cela au chinois, où il y a plus de 20 000 glyphes définis et cette définition est incomplète. Depuis ses débuts en morse et en code Baudot, la simplicité globale (peu de glyphes, fréquence statistique d'apparition) de la langue anglaise en a fait la lingua franca de l'ère numérique. Mais à mesure que le nombre de personnes entrant dans l'ère numérique a augmenté, le nombre de locuteurs non anglophones a augmenté. Au fur et à mesure que les chiffres augmentaient,de plus en plus de gens étaient de moins en moins enclins à accepter que les ordinateurs utilisent l'ASCII et ne parlent que l'anglais. Cela a considérablement augmenté le nombre d'ordinateurs "caractères" nécessaires pour comprendre. En conséquence, le nombre de glyphes encodés par les ordinateurs a dû doubler.

Le nombre de caractères disponibles a doublé lorsque le vénérable code ASCII 7 bits a été incorporé dans un encodage de caractères 8 bits appelé ISO Latin-1 (ou ISO 8859_1, «ISO» étant l'Organisation internationale de normalisation). Comme vous l'avez peut-être compris par le nom d'encodage, cette norme a permis la représentation de nombreuses langues dérivées du latin utilisées sur le continent européen. Cependant, ce n'est pas parce que la norme a été créée qu'elle était utilisable. À l'époque, de nombreux ordinateurs avaient déjà commencé à utiliser les 128 autres «caractères» qui pourraient être représentés par un caractère 8 bits avec un certain avantage. Les deux exemples survivants de l'utilisation de ces caractères supplémentaires sont l'ordinateur personnel IBM (PC) et le terminal informatique le plus populaire de tous les temps, le Digital Equipment Corporation VT-100.Ce dernier vit sous la forme d'un logiciel d'émulation de terminal.

L'heure réelle de la mort du personnage 8 bits sera sans aucun doute débattue pendant des décennies, mais je l'ai fixée lors de l'introduction de l'ordinateur Macintosh en 1984. Le Macintosh a introduit deux concepts très révolutionnaires dans l'informatique grand public: les polices de caractères stockées dans RAM; et WorldScript, qui pourrait être utilisé pour représenter des caractères dans n'importe quel langage. Bien sûr, il ne s'agissait que d'une copie de ce que Xerox avait expédié sur ses machines de classe Dandelion sous la forme du système de traitement de texte Star, mais le Macintosh a apporté ces nouveaux jeux de caractères et polices à un public qui utilisait encore des terminaux «stupides» . Une fois lancée, l'utilisation de polices différentes ne pouvait plus être arrêtée - c'était trop attrayant pour trop de gens. À la fin des années 80,la pression pour standardiser l'utilisation de tous ces caractères a atteint son paroxysme avec la formation du Consortium Unicode, qui a publié sa première spécification en 1990. Malheureusement, dans les années 80 et même dans les années 90, le nombre de jeux de caractères s'est multiplié. Très peu d'ingénieurs qui créaient de nouveaux codes de caractères à l'époque considéraient le standard Unicode naissant comme viable, et ils ont donc créé leurs propres mappages de codes en glyphes. Ainsi, alors que Unicode n'était pas bien accepté, l'idée qu'il n'y avait que 128 ou au plus 256 caractères disponibles avait définitivement disparu. Après le Macintosh, la prise en charge de différentes polices est devenue une fonctionnalité incontournable du traitement de texte. Les personnages de huit bits étaient en train de disparaître.Dans les années 80 et même dans les années 90, le nombre de jeux de caractères s'est multiplié. Très peu d'ingénieurs qui créaient de nouveaux codes de caractères à l'époque considéraient le standard Unicode naissant comme viable, et ils ont donc créé leurs propres mappages de codes en glyphes. Ainsi, alors que Unicode n'était pas bien accepté, l'idée qu'il n'y avait que 128 ou tout au plus 256 caractères disponibles avait définitivement disparu. Après le Macintosh, la prise en charge de différentes polices est devenue une fonctionnalité incontournable du traitement de texte. Les personnages de huit bits s'éteignaient.Dans les années 80 et même dans les années 90, le nombre de jeux de caractères s'est multiplié. Très peu d'ingénieurs qui créaient de nouveaux codes de caractères à l'époque considéraient le standard Unicode naissant comme viable, et ils ont donc créé leurs propres mappages de codes en glyphes. Ainsi, alors que Unicode n'était pas bien accepté, l'idée qu'il n'y avait que 128 ou au plus 256 caractères disponibles avait définitivement disparu. Après le Macintosh, la prise en charge de différentes polices est devenue une fonctionnalité incontournable du traitement de texte. Les personnages de huit bits s'éteignaient.l'idée qu'il n'y avait que 128 ou au plus 256 caractères disponibles avait définitivement disparu. Après le Macintosh, la prise en charge de différentes polices est devenue une fonctionnalité incontournable du traitement de texte. Les personnages de huit bits étaient en train de disparaître.l'idée qu'il n'y avait que 128 ou au plus 256 caractères disponibles avait définitivement disparu. Après le Macintosh, la prise en charge de différentes polices est devenue une fonctionnalité incontournable du traitement de texte. Les personnages de huit bits s'éteignaient.

Java et Unicode

Je suis entré dans l'histoire en 1992 lorsque j'ai rejoint le groupe Oak (le langage Java s'appelait Oak lors de son développement) chez Sun. Le type de base chara été défini comme étant 16 bits non signés, le seul type non signé en Java. La justification du caractère 16 bits était qu'il prendrait en charge toute représentation de caractère Unicode, rendant ainsi Java approprié pour représenter des chaînes dans n'importe quel langage pris en charge par Unicode. Mais être capable de représenter la chaîne et de pouvoir l'imprimer ont toujours été des problèmes distincts. Étant donné que la majeure partie de l'expérience du groupe Oak provenait des systèmes Unix et des systèmes dérivés d'Unix, le jeu de caractères le plus confortable était, encore une fois, ISO Latin-1. De plus, avec l'héritage Unix du groupe, le système d'E / S Java a été modélisé en grande partie sur l'abstraction de flux Unix où chaque périphérique d'E / S pouvait être représenté par un flux d'octets de 8 bits. Cette combinaison a laissé une certaine anomalie dans la langue entre un périphérique d'entrée 8 bits et les caractères 16 bits de Java. Donc,partout où des chaînes Java devaient être lues ou écrites dans un flux 8 bits, il y avait un petit bout de code, un hack, pour mapper comme par magie des caractères 8 bits en unicode 16 bits.

Dans les versions 1.0 du Java Developer Kit (JDK), le hack d'entrée était dans la DataInputStreamclasse, et le hack de sortie était la PrintStreamclasse entière . (En fait, il y avait une classe d'entrée nommée TextInputStreamdans la version alpha 2 de Java, mais elle a été supplantée par le DataInputStreampiratage de la version actuelle.) Cela continue de poser des problèmes aux programmeurs Java débutants, car ils recherchent désespérément l'équivalent Java du C fonction getc(). Considérez le programme Java 1.0 suivant:

import java.io. *; public class bidon {public static void main (String args []) {FileInputStream fis; DataInputStream dis; char c; essayez {fis = new FileInputStream ("data.txt"); dis = new DataInputStream (fis); while (vrai) {c = dis.readChar (); System.out.print (c); System.out.flush (); if (c == '\ n') break; } fis.close (); } catch (Exception e) {} System.exit (0); }}

À première vue, ce programme semble ouvrir un fichier, le lire un caractère à la fois et se terminer lorsque la première nouvelle ligne est lue. Cependant, en pratique, vous obtenez une sortie indésirable. Et la raison pour laquelle vous obtenez des fichiers indésirables est que readChar lit les caractères Unicode 16 bits et System.out.printimprime ce qu'il suppose être des caractères ISO Latin-1 8 bits. Cependant, si vous modifiez le programme ci-dessus pour utiliser la fonction readLine de DataInputStream, il semblera fonctionner car le code de readLinelit un format défini avec un signe de passage à la spécification Unicode comme «UTF-8 modifié». (UTF-8 est le format spécifié par Unicode pour représenter les caractères Unicode dans un flux d'entrée 8 bits.) Ainsi, la situation dans Java 1.0 est que les chaînes Java sont composées de caractères Unicode 16 bits, mais il n'y a qu'un seul mappage qui mappe Caractères ISO Latin-1 en Unicode. Heureusement, Unicode définit la page de codes "0" - c'est-à-dire les 256 caractères dont les 8 bits supérieurs sont tous à zéro - pour correspondre exactement à l'ensemble ISO Latin-1. Ainsi, le mappage est assez simple, et tant que vous n'utilisez que des fichiers de caractères ISO Latin-1, vous n'aurez aucun problème lorsque les données quittent un fichier, sont manipulées par une classe Java, puis réécrites dans un fichier. .

There were two problems with burying the input conversion code into these classes: Not all platforms stored their multilingual files in modified UTF-8 format; and certainly, the applications on these platforms didn't necessarily expect non-Latin characters in this form. Therefore, the implementation support was incomplete, and there was no easy way to add the needed support in a later release.

Java 1.1 and Unicode

The Java 1.1 release introduced an entirely new set of interfaces for handling characters, called Readers and Writers. I modified the class named bogus from above into a class named cool. The cool class uses an InputStreamReader class to process the file rather than the DataInputStream class. Note that InputStreamReader is a subclass of the new Reader class and the System.out is now a PrintWriter object, which is a subclass of the Writer class. The code for this example is shown below:

import java.io.*; public class cool { public static void main(String args[]) { FileInputStream fis; InputStreamReader irs; char c; try { fis = new FileInputStream("data.txt"); irs = new InputStreamReader(fis); System.out.println("Using encoding : "+irs.getEncoding()); while (true) { c = (char) irs.read(); System.out.print(c); System.out.flush(); if (c == '\n') break; } fis.close(); } catch (Exception e) { } System.exit(0); } } 

The primary difference between this example and the previous code listing is the use of the InputStreamReader class rather than the DataInputStream class. Another way in which this example is different from the previous one is that there is an additional line that prints out the encoding used by the InputStreamReader class.

The important point is that the existing code, once undocumented (and ostensibly unknowable) and embedded inside the implementation of the getChar method of the DataInputStream class, has been removed (actually its use is deprecated; it will be removed in a future release). In the 1.1 version of Java, the mechanism that performs the conversion is now encapsulated in the Reader class. This encapsulation provides a way for the Java class libraries to support many different external representations of non-Latin characters while always using Unicode internally.

Of course, like the original I/O subsystem design, there are symmetric counterparts to the reading classes that perform writing. The class OutputStreamWriter can be used to write strings to an output stream, the class BufferedWriter adds a layer of buffering, and so on.

Trading warts or real progress?

The somewhat lofty goal of the design of the Reader and Writerclasses was to tame what is currently a hodge-podge of representation standards for the same information by providing a standard way of converting back and forth between the legacy representation -- be it Macintosh Greek or Windows Cyrillic -- and Unicode. So, a Java class that deals with strings need not change when it moves from platform to platform. This might be the end of the story, except that now that the conversion code is encapsulated, the question arises as to what that code assumes.

En recherchant cette chronique, je me suis souvenu d'une citation célèbre d'un cadre de Xerox (avant Xerox, quand c'était la Haloid Company) sur le fait que le photocopieur était superflu car il était assez facile pour une secrétaire de mettre un morceau de papier carbone dans sa machine à écrire et faire une copie d'un document pendant qu'elle créait l'original. Bien sûr, ce qui est évident avec le recul, c'est que la photocopieuse profite beaucoup plus à la personne qui reçoit un document qu'à une personne qui produit un document. JavaSoft a montré un manque similaire d'informations sur l'utilisation des classes d'encodage et de décodage de caractères dans leur conception de cette partie du système.