Uno sguardo approfondito al tipo di carattere di Java

La versione 1.1 di Java introduce una serie di classi per trattare i personaggi. Queste nuove classi creano un'astrazione per la conversione da una nozione di valori di carattere specifica della piattaforma in valori Unicode . Questa colonna esamina ciò che è stato aggiunto e le motivazioni per l'aggiunta di queste classi di personaggi.

Digita char

Forse il tipo di base più abusato nel linguaggio C è il tipo char . Il tipo char è abusato in parte perché è definito come 8 bit e negli ultimi 25 anni 8 bit ha anche definito il più piccolo blocco indivisibile di memoria sui computer. Quando si combina quest'ultimo fatto con il fatto che il set di caratteri ASCII è stato definito per adattarsi a 7 bit, il tipo char rende un tipo "universale" molto conveniente. Inoltre, in C, un puntatore a una variabile di tipo char è diventato il tipo di puntatore universale perché tutto ciò che potrebbe essere referenziato come char potrebbe anche essere referenziato come qualsiasi altro tipo attraverso l'uso del casting.

L'uso e l'abuso del tipo char nel linguaggio C hanno portato a molte incompatibilità tra le implementazioni del compilatore, quindi nello standard ANSI per C sono state apportate due modifiche specifiche: Il puntatore universale è stato ridefinito per avere un tipo di void, richiedendo quindi un esplicito dichiarazione del programmatore; e il valore numerico dei caratteri è stato considerato come segno, definendo così come sarebbero stati trattati se usati nei calcoli numerici. Poi, a metà degli anni '80, ingegneri e utenti hanno capito che 8 bit erano insufficienti per rappresentare tutti i personaggi del mondo. Sfortunatamente, a quel punto, C era così radicato che le persone non erano disposte, forse addirittura incapaci, a modificare la definizione del chargenere. Ora flash forward agli anni '90, ai primi inizi di Java. Uno dei tanti principi stabiliti nella progettazione del linguaggio Java era che i caratteri sarebbero stati 16 bit. Questa scelta supporta l'uso di Unicode , un modo standard di rappresentare molti tipi diversi di caratteri in molte lingue diverse. Purtroppo, ha anche posto le basi per una serie di problemi che solo ora vengono risolti.

Cos'è un personaggio comunque?

Sapevo che era in difficoltà quando mi sono trovato a fare la domanda, "Che cosa è un personaggio?" Bene, un personaggio è una lettera, giusto? Un mucchio di lettere compone una parola, le parole formano frasi e così via. La realtà, tuttavia, è che la relazione tra la rappresentazione di un personaggio sullo schermo di un computer, chiamato il suo glifo , e il valore numerico che specifica quel glifo, chiamato a code point, non è affatto semplice.

Mi considero fortunato ad essere un madrelingua inglese. Primo, perché era il linguaggio comune di un numero significativo di coloro che hanno contribuito alla progettazione e allo sviluppo del computer digitale moderno; secondo, perché ha un numero relativamente piccolo di glifi. Esistono 96 caratteri stampabili nella definizione ASCII che possono essere utilizzati per scrivere in inglese. Confronta questo con il cinese, dove sono definiti oltre 20.000 glifi e quella definizione è incompleta. Fin dai primi inizi del codice Morse e Baudot, la semplicità complessiva (pochi glifi, frequenza statistica di apparizione) della lingua inglese l'ha resa la lingua franca dell'era digitale. Ma poiché il numero di persone che entrano nell'era digitale è aumentato, è aumentato anche il numero di persone che non parlano inglese. Man mano che i numeri crescevano,sempre più persone erano sempre più riluttanti ad accettare che i computer usassero ASCII e parlassero solo inglese. Ciò aumentò notevolmente il numero di "personaggi" che i computer dovevano comprendere. Di conseguenza, il numero di glifi codificati dai computer doveva raddoppiare.

Il numero di caratteri disponibili è raddoppiato quando il venerabile codice ASCII a 7 bit è stato incorporato in una codifica di caratteri a 8 bit chiamata ISO Latin-1 (o ISO 8859_1, "ISO" è l'International Standards Organization). Come avrai potuto intuire dal nome della codifica, questo standard consentiva la rappresentazione di molte delle lingue di derivazione latina utilizzate nel continente europeo. Tuttavia, solo perché lo standard è stato creato, non significava che fosse utilizzabile. A quel tempo, molti computer avevano già iniziato a utilizzare gli altri 128 "caratteri" che potrebbero essere rappresentati da un carattere a 8 bit con un certo vantaggio. I due esempi sopravvissuti dell'uso di questi caratteri extra sono il Personal Computer (PC) IBM e il terminale per computer più popolare di sempre, la Digital Equipment Corporation VT-100.Quest'ultimo vive sotto forma di software di emulazione di terminale.

L'ora della morte effettiva per il carattere a 8 bit sarà senza dubbio discussa per decenni, ma l'ho fissata all'introduzione del computer Macintosh nel 1984. Il Macintosh ha introdotto due concetti molto rivoluzionari nell'informatica tradizionale: i caratteri dei caratteri che sono stati memorizzati in RAM; e WorldScript, che potrebbe essere utilizzato per rappresentare caratteri in qualsiasi lingua. Naturalmente, questa era semplicemente una copia di ciò che Xerox aveva spedito sulle sue macchine di classe Dandelion sotto forma del sistema di elaborazione testi Star, ma il Macintosh portò questi nuovi set di caratteri e font a un pubblico che utilizzava ancora terminali "stupidi" . Una volta avviato, l'uso di caratteri diversi non poteva essere interrotto: era semplicemente troppo attraente per troppe persone. Alla fine degli anni '80,la pressione per standardizzare l'uso di tutti questi caratteri è venuta al culmine con la formazione dell'Unicode Consortium, che ha pubblicato la sua prima specifica nel 1990. Sfortunatamente, durante gli anni '80 e anche negli anni '90, il numero di set di caratteri si è moltiplicato. Pochissimi degli ingegneri che all'epoca stavano creando nuovi codici di caratteri consideravano il nascente standard Unicode praticabile, e così crearono le proprie mappature di codici in glifi. Quindi, sebbene Unicode non fosse ben accettato, l'idea che c'erano solo 128 o al massimo 256 caratteri disponibili era definitivamente svanita. Dopo il Macintosh, il supporto per diversi tipi di carattere è diventato una funzionalità indispensabile per l'elaborazione di testi. I caratteri a otto bit stavano scomparendo.Negli anni '80 e anche negli anni '90, il numero di set di caratteri si è moltiplicato. Pochissimi degli ingegneri che all'epoca stavano creando nuovi codici di caratteri consideravano il nascente standard Unicode praticabile, e così crearono le proprie mappature di codici in glifi. Quindi, sebbene Unicode non fosse ben accettato, l'idea che c'erano solo 128 o al massimo 256 caratteri disponibili era definitivamente svanita. Dopo il Macintosh, il supporto per diversi tipi di carattere è diventato una funzionalità indispensabile per l'elaborazione di testi. I caratteri a otto bit stavano scomparendo.Negli anni '80 e anche negli anni '90, il numero di set di caratteri si è moltiplicato. Pochissimi degli ingegneri che all'epoca stavano creando nuovi codici di caratteri consideravano il nascente standard Unicode praticabile, e così crearono le proprie mappature di codici in glifi. Quindi, sebbene Unicode non fosse ben accettato, l'idea che c'erano solo 128 o al massimo 256 caratteri disponibili era definitivamente svanita. Dopo il Macintosh, il supporto per diversi tipi di carattere è diventato una funzionalità indispensabile per l'elaborazione di testi. I caratteri a otto bit stavano scomparendo.l'idea che c'erano solo 128 o al massimo 256 caratteri disponibili era definitivamente svanita. Dopo il Macintosh, il supporto per diversi tipi di carattere è diventato una funzionalità indispensabile per l'elaborazione di testi. I caratteri a otto bit stavano scomparendo.l'idea che c'erano solo 128 o al massimo 256 caratteri disponibili era definitivamente svanita. Dopo il Macintosh, il supporto per diversi tipi di carattere è diventato una funzionalità indispensabile per l'elaborazione di testi. I caratteri a otto bit stavano scomparendo.

Java e Unicode

Sono entrato nella storia nel 1992 quando sono entrato a far parte del gruppo Oak (il linguaggio Java si chiamava Oak quando è stato sviluppato per la prima volta) alla Sun. Il tipo di base charera definito come 16 bit senza segno, l'unico tipo senza segno in Java. La logica per il carattere a 16 bit era che avrebbe supportato qualsiasi rappresentazione di caratteri Unicode, rendendo così Java adatto per rappresentare stringhe in qualsiasi linguaggio supportato da Unicode. Ma riuscire a rappresentare la stringa e poterla stampare sono sempre stati problemi separati. Dato che la maggior parte dell'esperienza nel gruppo Oak proveniva da sistemi Unix e da sistemi derivati ​​da Unix, il set di caratteri più comodo era, ancora una volta, ISO Latin-1. Inoltre, con l'eredità Unix del gruppo, il sistema I / O Java è stato modellato in gran parte sull'astrazione del flusso Unix per cui ogni dispositivo I / O poteva essere rappresentato da un flusso di byte a 8 bit. Questa combinazione ha lasciato qualcosa di sbagliato nel linguaggio tra un dispositivo di input a 8 bit e i caratteri a 16 bit di Java. Quindi,ovunque le stringhe Java dovevano essere lette o scritte su un flusso a 8 bit, c'era un piccolo pezzo di codice, un trucco, per mappare magicamente caratteri a 8 bit in unicode a 16 bit.

Nelle versioni 1.0 del Java Developer Kit (JDK), l'hack dell'input era nella DataInputStreamclasse e l'hack dell'output era l'intera PrintStreamclasse. (In realtà c'era una classe di input denominata TextInputStreamnella versione alpha 2 di Java, ma è stata soppiantata DataInputStreamdall'hacking nella versione attuale.) Ciò continua a causare problemi ai programmatori Java principianti, poiché cercano disperatamente l'equivalente Java del C funzione getc(). Considera il seguente programma Java 1.0:

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

A prima vista, questo programma sembrerebbe aprire un file, leggerlo un carattere alla volta e uscire quando viene letto il primo ritorno a capo. Tuttavia, in pratica, ciò che ottieni è output spazzatura. E il motivo per cui ottieni spazzatura è che readChar legge caratteri Unicode a 16 bit e System.out.printstampa quelli che presume siano caratteri ISO Latin-1 a 8 bit. Tuttavia, se si modifica il programma precedente per utilizzare la funzione readLine di DataInputStream, sembrerà funzionare perché il codice in readLinelegge un formato definito con un cenno del capo alla specifica Unicode come "UTF-8 modificato". (UTF-8 è il formato che Unicode specifica per rappresentare i caratteri Unicode in un flusso di input a 8 bit.) Quindi la situazione in Java 1.0 è che le stringhe Java sono composte da caratteri Unicode a 16 bit, ma c'è solo una mappatura che mappa Caratteri ISO Latin-1 in Unicode. Fortunatamente, Unicode definisce la tabella codici "0", ovvero i 256 caratteri i cui 8 bit superiori sono tutti zero, in modo che corrispondano esattamente al set ISO Latin-1. Pertanto, la mappatura è piuttosto banale e fintanto che utilizzi solo file di caratteri ISO Latin-1, non avrai problemi quando i dati lasciano un file, vengono manipolati da una classe Java e quindi riscritti in un file .

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.

Durante la ricerca di questo articolo, mi è venuta in mente una famosa citazione di un dirigente Xerox (prima che fosse Xerox, quando era la Haloid Company) sulla fotocopiatrice superflua perché era abbastanza facile per una segretaria mettere un pezzo di carta carbone la sua macchina da scrivere e fare una copia di un documento mentre stava creando l'originale. Naturalmente, ciò che è ovvio col senno di poi è che la fotocopiatrice avvantaggia la persona che riceve un documento molto più di una persona che genera un documento. JavaSoft ha mostrato una simile mancanza di comprensione dell'uso delle classi di codifica e decodifica dei caratteri nella progettazione di questa parte del sistema.