Ein detaillierter Blick auf den Zeichentyp von Java

Die Version 1.1 von Java bietet eine Reihe von Klassen für den Umgang mit Zeichen. Diese neuen Klassen erstellen eine Abstraktion für die Konvertierung von einem plattformspezifischen Begriff von Zeichenwerten in Unicode- Werte. In dieser Spalte werden die hinzugefügten Elemente und die Motivationen für das Hinzufügen dieser Zeichenklassen beschrieben.

Geben Sie char ein

Der vielleicht am meisten missbrauchte Basistyp in der C-Sprache ist der Typ char . Der char- Typ wird teilweise missbraucht, weil er als 8 Bit definiert ist, und in den letzten 25 Jahren haben 8 Bit auch den kleinsten unteilbaren Speicherblock auf Computern definiert. Wenn Sie die letztere Tatsache mit der Tatsache kombinieren, dass der ASCII-Zeichensatz so definiert wurde, dass er in 7 Bit passt, ist der char- Typ ein sehr praktischer "universeller" Typ. Ferner wurde in C ein Zeiger auf eine Variable vom Typ char zum universellen Zeigertyp, da alles, was als char bezeichnet werden konnte, durch Verwendung von Casting auch als jeder andere Typ referenziert werden konnte.

Die Verwendung und der Missbrauch des char- Typs in der C-Sprache führten zu vielen Inkompatibilitäten zwischen Compiler-Implementierungen. Daher wurden im ANSI-Standard für C zwei spezifische Änderungen vorgenommen: Der universelle Zeiger wurde neu definiert, um einen Typ von void zu haben, sodass eine explizite erforderlich war Erklärung des Programmierers; und der numerische Wert von Zeichen wurde als signiert angesehen, wodurch definiert wurde, wie sie behandelt werden würden, wenn sie in numerischen Berechnungen verwendet würden. Mitte der 1980er Jahre stellten Ingenieure und Benutzer fest, dass 8 Bit nicht ausreichten, um alle Charaktere der Welt darzustellen. Leider war C zu diesem Zeitpunkt so tief verwurzelt, dass die Leute nicht bereit oder vielleicht sogar nicht in der Lage waren, die Definition des Zeichens zu ändernArt. Nun blicken wir auf die 90er Jahre zurück, auf die frühen Anfänge von Java. Eines der vielen Prinzipien, die im Design der Java-Sprache festgelegt wurden, war, dass Zeichen 16 Bit groß sein würden. Diese Auswahl unterstützt die Verwendung von Unicode , einer Standardmethode zur Darstellung vieler verschiedener Arten von Zeichen in vielen verschiedenen Sprachen. Leider hat es auch die Voraussetzungen für eine Vielzahl von Problemen geschaffen, die erst jetzt behoben werden.

Was ist überhaupt ein Charakter?

Ich wusste, dass ich in Schwierigkeiten war, als ich die Frage stellte: "Also, was ist ein Charakter?" Nun, ein Charakter ist ein Buchstabe, oder? Eine Reihe von Buchstaben bilden ein Wort, Wörter bilden Sätze und so weiter. Die Realität ist jedoch, dass die Beziehung zwischen der Darstellung eines Zeichens auf einem Computerbildschirm, der als Glyphe bezeichnet wird , und dem numerischen Wert, der angibt, dass die Glyphe a genannt code pointwird, überhaupt nicht einfach ist.

Ich schätze mich glücklich, ein Muttersprachler der englischen Sprache zu sein. Erstens, weil es die gemeinsame Sprache einer bedeutenden Anzahl von Personen war, die zum Design und zur Entwicklung des modernen digitalen Computers beigetragen haben. zweitens, weil es eine relativ kleine Anzahl von Glyphen hat. Die ASCII-Definition enthält 96 druckbare Zeichen, mit denen Englisch geschrieben werden kann. Vergleichen Sie dies mit Chinesisch, wo über 20.000 Glyphen definiert sind und diese Definition unvollständig ist. Von den frühen Anfängen des Morse- und Baudot-Codes an hat die allgemeine Einfachheit (wenige Glyphen, statistische Häufigkeit des Auftretens) die englische Sprache zur Verkehrssprache des digitalen Zeitalters gemacht. Aber mit der Zahl der Menschen, die in das digitale Zeitalter eintreten, hat auch die Zahl der nicht-englischen Muttersprachler zugenommen. Als die Zahlen wuchsen,Immer mehr Menschen waren zunehmend nicht bereit zu akzeptieren, dass Computer ASCII verwendeten und nur Englisch sprachen. Dies erhöhte die Anzahl der "Zeichen" -Computer, die zum Verstehen benötigt wurden, erheblich. Infolgedessen musste sich die Anzahl der von Computern codierten Glyphen verdoppeln.

Die Anzahl der verfügbaren Zeichen hat sich verdoppelt, als der ehrwürdige 7-Bit-ASCII-Code in eine 8-Bit-Zeichencodierung namens ISO Latin-1 (oder ISO 8859_1, wobei "ISO" die International Standards Organization ist) integriert wurde. Wie Sie vielleicht anhand des Kodierungsnamens erfahren haben, ermöglichte dieser Standard die Darstellung vieler lateinischer Sprachen, die auf dem europäischen Kontinent verwendet werden. Nur weil der Standard erstellt wurde, bedeutete dies nicht, dass er verwendbar war. Zu diesem Zeitpunkt hatten viele Computer bereits damit begonnen, die anderen 128 "Zeichen" zu verwenden, die möglicherweise durch ein 8-Bit-Zeichen dargestellt werden. Die beiden erhaltenen Beispiele für die Verwendung dieser zusätzlichen Zeichen sind der IBM Personal Computer (PC) und das beliebteste Computerterminal aller Zeiten, die Digital Equipment Corporation VT-100.Letzteres lebt in Form von Terminalemulatorsoftware weiter.

Der tatsächliche Zeitpunkt des Todes des 8-Bit-Zeichens wird zweifellos jahrzehntelang diskutiert, aber ich habe ihn bei der Einführung des Macintosh-Computers im Jahr 1984 festgelegt. Der Macintosh brachte zwei sehr revolutionäre Konzepte in das Mainstream-Computing ein: Zeichensätze, die in gespeichert wurden RAM; und WorldScript, mit dem Zeichen in jeder Sprache dargestellt werden können. Natürlich war dies nur eine Kopie dessen, was Xerox auf seinen Maschinen der Dandelion-Klasse in Form des Star-Textverarbeitungssystems ausgeliefert hatte, aber der Macintosh brachte diese neuen Zeichensätze und Schriftarten einem Publikum, das immer noch "dumme" Terminals verwendete . Einmal gestartet, konnte die Verwendung verschiedener Schriftarten nicht gestoppt werden - es war einfach zu ansprechend für zu viele Menschen. In den späten 80ernDer Druck, die Verwendung all dieser Zeichen zu standardisieren, spitzte sich mit der Gründung des Unicode-Konsortiums zu, das 1990 seine erste Spezifikation veröffentlichte. Leider vervielfachte sich die Anzahl der Zeichensätze in den 80er und sogar in den 90er Jahren. Nur sehr wenige Ingenieure, die zu dieser Zeit neue Zeichencodes erstellten, hielten den entstehenden Unicode-Standard für realisierbar, und so erstellten sie ihre eigenen Zuordnungen von Codes zu Glyphen. Obwohl Unicode nicht gut angenommen wurde, war die Vorstellung, dass nur 128 oder höchstens 256 Zeichen verfügbar waren, definitiv verschwunden. Nach dem Macintosh wurde die Unterstützung verschiedener Schriftarten zu einem Muss für die Textverarbeitung. Acht-Bit-Zeichen waren vom Aussterben bedroht.In den 80er und sogar in den 90er Jahren vervielfachte sich die Anzahl der Zeichensätze. Nur sehr wenige Ingenieure, die zu dieser Zeit neue Zeichencodes erstellten, hielten den entstehenden Unicode-Standard für realisierbar, und so erstellten sie ihre eigenen Zuordnungen von Codes zu Glyphen. Obwohl Unicode nicht gut angenommen wurde, war die Vorstellung, dass nur 128 oder höchstens 256 Zeichen verfügbar waren, definitiv verschwunden. Nach dem Macintosh wurde die Unterstützung verschiedener Schriftarten zu einem Muss für die Textverarbeitung. Acht-Bit-Zeichen waren vom Aussterben bedroht.In den 80er und sogar in den 90er Jahren vervielfachte sich die Anzahl der Zeichensätze. Nur sehr wenige Ingenieure, die zu dieser Zeit neue Zeichencodes erstellten, hielten den entstehenden Unicode-Standard für praktikabel, und so erstellten sie ihre eigenen Zuordnungen von Codes zu Glyphen. Obwohl Unicode nicht gut angenommen wurde, war die Vorstellung, dass nur 128 oder höchstens 256 Zeichen verfügbar waren, definitiv verschwunden. Nach dem Macintosh wurde die Unterstützung verschiedener Schriftarten zu einem Muss für die Textverarbeitung. Acht-Bit-Zeichen waren vom Aussterben bedroht.Die Vorstellung, dass nur 128 oder höchstens 256 Zeichen verfügbar waren, war definitiv verschwunden. Nach dem Macintosh wurde die Unterstützung verschiedener Schriftarten zu einem Muss für die Textverarbeitung. Acht-Bit-Zeichen waren vom Aussterben bedroht.Die Vorstellung, dass nur 128 oder höchstens 256 Zeichen verfügbar waren, war definitiv verschwunden. Nach dem Macintosh wurde die Unterstützung verschiedener Schriftarten zu einem Muss für die Textverarbeitung. Acht-Bit-Zeichen waren vom Aussterben bedroht.

Java und Unicode

Ich trat 1992 in die Geschichte ein, als ich mich bei Oak der Oak-Gruppe anschloss (die Java-Sprache hieß Oak, als sie zum ersten Mal entwickelt wurde). Der Basistyp charwurde als 16 vorzeichenlose Bits definiert, der einzige vorzeichenlose Typ in Java. Der Grund für das 16-Bit-Zeichen war, dass es jede Unicode-Zeichendarstellung unterstützen würde, wodurch Java für die Darstellung von Zeichenfolgen in jeder von Unicode unterstützten Sprache geeignet wäre. Die Möglichkeit, die Zeichenfolge darzustellen und zu drucken, war jedoch immer ein Problem. Da der größte Teil der Erfahrung in der Oak-Gruppe von Unix-Systemen und von Unix abgeleiteten Systemen stammte, war der komfortabelste Zeichensatz wiederum ISO Latin-1. Mit dem Unix-Erbe der Gruppe wurde das Java-E / A-System zu einem großen Teil der Unix-Stream-Abstraktion nachempfunden, wobei jedes E / A-Gerät durch einen Stream von 8-Bit-Bytes dargestellt werden konnte. Diese Kombination hat in der Sprache zwischen einem 8-Bit-Eingabegerät und den 16-Bit-Zeichen von Java eine Art Fehlfunktion hinterlassen. So,Überall dort, wo Java-Zeichenfolgen aus einem 8-Bit-Stream gelesen oder in diesen geschrieben werden mussten, gab es ein kleines Stück Code, einen Hack, um 8-Bit-Zeichen auf magische Weise in 16-Bit-Unicode abzubilden.

In den 1.0-Versionen des Java Developer Kit (JDK) befand sich der Eingabe-Hack in der DataInputStreamKlasse und der Ausgabe-Hack in der gesamten PrintStreamKlasse. (Tatsächlich gab es TextInputStreamin der Alpha 2-Version von Java eine Eingabeklasse , die jedoch durch den DataInputStreamHack in der tatsächlichen Version ersetzt wurde.) Dies verursacht weiterhin Probleme für Anfänger von Java-Programmierern, da sie verzweifelt nach dem Java-Äquivalent von C suchen Funktion getc(). Betrachten Sie das folgende Java 1.0-Programm:

import java.io. *; Schein der öffentlichen Klasse {public static void main (String args []) {FileInputStream fis; DataInputStream dis; char c; try {fis = new FileInputStream ("data.txt"); dis = neuer DataInputStream (fis); while (true) {c = dis.readChar (); System.out.print (c); System.out.flush (); if (c == '\ n') break; } fis.close (); } catch (Ausnahme e) {} System.exit (0); }}

Auf den ersten Blick scheint dieses Programm eine Datei zu öffnen, sie zeichenweise zu lesen und zu beenden, wenn die erste neue Zeile gelesen wird. In der Praxis erhalten Sie jedoch eine Junk-Ausgabe. Und der Grund, warum Sie Junk bekommen, ist, dass readChar 16-Bit-Unicode-Zeichen liest und System.out.printdruckt, was angenommen wird, dass es sich um ISO Latin-1 8-Bit-Zeichen handelt. Wenn Sie jedoch das obige Programm ändern, um die readLine- Funktion von zu verwenden DataInputStream, scheint es zu funktionieren, da der Code in readLine enthalten istliest ein Format, das mit einer Übergabe an die Unicode-Spezifikation als "modifiziertes UTF-8" definiert ist. (UTF-8 ist das Format, das Unicode für die Darstellung von Unicode-Zeichen in einem 8-Bit-Eingabestream angibt.) In Java 1.0 besteht die Situation also darin, dass Java-Zeichenfolgen aus 16-Bit-Unicode-Zeichen bestehen, es jedoch nur eine Zuordnung gibt, die zugeordnet wird ISO Latin-1 Zeichen in Unicode. Glücklicherweise definiert Unicode die Codepage "0" - dh die 256 Zeichen, deren obere 8 Bits alle Null sind - so, dass sie genau dem ISO Latin-1-Satz entsprechen. Daher ist die Zuordnung ziemlich trivial. Solange Sie nur ISO Latin-1-Zeichendateien verwenden, treten keine Probleme auf, wenn die Daten eine Datei verlassen, von einer Java-Klasse bearbeitet und dann in eine Datei umgeschrieben werden .

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.

Während ich diese Kolumne recherchierte, wurde ich an ein berühmtes Zitat eines Xerox-Managers (bevor es Xerox war, als es die Haloid Company war) erinnert, dass der Fotokopierer überflüssig sei, weil es für eine Sekretärin ziemlich einfach war, ein Stück Kohlepapier einzulegen ihre Schreibmaschine und machen Sie eine Kopie eines Dokuments, während sie das Original erstellt. Im Nachhinein ist natürlich klar, dass das Fotokopiergerät der Person, die ein Dokument erhält, viel mehr Vorteile bringt als einer Person, die ein Dokument erstellt. JavaSoft hat einen ähnlichen Mangel an Einsicht in die Verwendung der Zeichencodierungs- und -decodierungsklassen bei der Gestaltung dieses Teils des Systems gezeigt.