Programmering

Et dybtgående kig på Java's karaktertype

1.1-versionen af ​​Java introducerer et antal klasser til håndtering af tegn. Disse nye klasser skaber en abstraktion til konvertering fra en platformsspecifik opfattelse af karakterværdier til Unicode værdier. Denne kolonne ser på, hvad der er tilføjet, og motivationen til at tilføje disse karakterklasser.

Type char

Måske er den mest misbrugte basistype på C-sproget typen char. Det char type misbruges delvist, fordi den er defineret til at være 8 bit, og i de sidste 25 år har 8 bit også defineret den mindste udelelige del af hukommelsen på computere. Når du kombinerer sidstnævnte kendsgerning med det faktum, at ASCII-tegnsættet blev defineret til at passe i 7 bits, blev char type er en meget bekvem "universel" type. Yderligere, i C, en markør til en variabel af typen char blev den universelle pointertype, fordi alt, hvad der kunne betegnes som en char kunne også betegnes som enhver anden type ved hjælp af støbning.

Brug og misbrug af char skriv i C-sproget førte til mange inkompatibiliteter mellem compilerimplementeringer, så i ANSI-standarden for C blev der foretaget to specifikke ændringer: Den universelle markør blev omdefineret til at have en type tomrum, hvilket kræver en eksplicit erklæring fra programmøren; og den numeriske værdi af tegn blev betragtet som underskrevet og definerede således, hvordan de ville blive behandlet, når de blev brugt i numeriske beregninger. Derefter, i midten af ​​1980'erne, regnede ingeniører og brugere ud, at 8 bit var utilstrækkelig til at repræsentere alle tegn i verden. Desværre var C på det tidspunkt så forankret, at folk ikke var villige til, måske endda ude af stand til, at ændre definitionen af char type. Blink nu frem til 90'erne til Java's tidlige begyndelse. Et af de mange principper, der blev fastlagt i designet af Java-sproget, var at tegn ville være 16 bits. Dette valg understøtter brugen af Unicode, en standard måde at repræsentere mange forskellige slags tegn på mange forskellige sprog. Desværre satte det også scenen for en række problemer, der først nu er afhjulpet.

Hvad er alligevel en karakter?

Jeg vidste, at jeg var i problemer, da jeg befandt mig ved at stille spørgsmålet: "Hvad så er et tegn? "Nå, et tegn er et bogstav, ikke? En flok bogstaver udgør et ord, ord danner sætninger og så videre. Virkeligheden er dog, at forholdet mellem repræsentationen af ​​et tegn på en computerskærm , kaldes dens glyph, til den numeriske værdi, der angiver den glyf, kaldet a kode punkt, er slet ikke rigtig ligetil.

Jeg betragter mig heldig som en indfødt taler det engelske sprog. For det første fordi det var det fælles sprog for et betydeligt antal af dem, der bidrog til designet og udviklingen af ​​den moderne digitale computer; for det andet, fordi det har et relativt lille antal glyfer. Der er 96 tegn, der kan udskrives i ASCII-definitionen, der kan bruges til at skrive engelsk. Sammenlign dette med kinesisk, hvor der er defineret over 20.000 tegn, og denne definition er ufuldstændig. Fra den tidlige begyndelse i Morse- og Baudot-koden har den overordnede enkelhed (få tegn, statistisk udseendefrekvens) af det engelske sprog gjort det til lingua-franca i den digitale tidsalder. Men i takt med at antallet af mennesker, der går ind i den digitale tidsalder, er steget, er antallet af ikke-indfødte engelsktalende også steget. Efterhånden som antallet voksede, blev flere og flere mennesker i stigende grad uvillige til at acceptere, at computere brugte ASCII og kun talte engelsk. Dette øgede antallet af "tegn" computere, der var nødvendige for at forstå, i høj grad. Som et resultat måtte antallet af glyfer kodet af computere fordobles.

Antallet af tilgængelige tegn fordobles, da den ærværdige 7-bit ASCII-kode blev inkorporeret i en 8-bit tegnkodning kaldet ISO Latin-1 (eller ISO 8859_1, "ISO" er Den Internationale Standardorganisation). Som du måske har samlet ved hjælp af kodningsnavnet, tillod denne standard repræsentation af mange af de latin-afledte sprog, der blev brugt på det europæiske kontinent. Bare fordi standarden blev oprettet, betød det imidlertid ikke, at den kunne bruges. På det tidspunkt var mange computere allerede begyndt at bruge de andre 128 "tegn", der muligvis repræsenteres af et 8-bit tegn til en vis fordel. De to overlevende eksempler på brugen af ​​disse ekstra tegn er IBM Personal Computer (PC) og den mest populære computerterminal nogensinde, Digital Equipment Corporation VT-100. Sidstnævnte lever videre i form af terminalemulatorsoftware.

Den aktuelle dødstidspunkt for 8-bit-karakteren vil uden tvivl blive drøftet i årtier, men jeg knytter den til ved introduktionen af ​​Macintosh-computeren i 1984. Macintosh'en bragte to meget revolutionerende koncepter ind i mainstream-computing: tegnskrifttyper, der blev gemt VÆDDER; og WorldScript, som kunne bruges til at repræsentere tegn på ethvert sprog. Selvfølgelig var dette simpelthen en kopi af, hvad Xerox havde sendt på sine Mælkebøtteklassemaskiner i form af Star-tekstbehandlingssystemet, men Macintosh bragte disse nye tegnsæt og skrifttyper til et publikum, der stadig brugte "dumme" terminaler . Når det var startet, kunne brugen af ​​forskellige skrifttyper ikke stoppes - det var bare for attraktivt for for mange mennesker. I slutningen af ​​80'erne kom presset til at standardisere brugen af ​​alle disse tegn på spidsen med dannelsen af ​​Unicode Consortium, der offentliggjorde sin første specifikation i 1990. Desværre i løbet af 80'erne og endog ud i 90'erne blev antal tegnsæt ganget. Meget få af de ingeniører, der skabte nye tegnkoder på det tidspunkt, betragtede den spirende Unicode-standard levedygtig, og derfor skabte de deres egne kortlægninger af koder til tegn. Så mens Unicode ikke blev godt accepteret, var forestillingen om, at der kun var 128 eller højst 256 tegn til rådighed, bestemt væk. Efter Macintosh blev understøttelse af forskellige skrifttyper en must-have-funktion til tekstbehandling. Otte bit tegn var ved at falme ud i udryddelse.

Java og Unicode

Jeg kom ind i historien i 1992, da jeg blev medlem af Oak-gruppen (Java-sproget blev kaldt Oak, da det først blev udviklet) hos Sun. Basistypen char blev defineret til at være 16 usignerede bits, den eneste usignerede type i Java. Begrundelsen for 16-bit karakteren var, at den ville understøtte enhver Unicode-tegnsætning, hvilket gør Java egnet til at repræsentere strenge på ethvert sprog, der understøttes af Unicode. Men at kunne repræsentere strengen og at kunne udskrive den har altid været separate problemer. I betragtning af at det meste af oplevelsen i Oak-gruppen kom fra Unix-systemer og Unix-afledte systemer, var det mest behagelige tegnsæt igen ISO Latin-1. Også med gruppens Unix-arv blev Java I / O-systemet stort set modelleret på Unix-stream-abstraktionen, hvorved hver I / O-enhed kunne repræsenteres af en strøm på 8-bit bytes. Denne kombination efterlod noget af en fejlfunktion i sproget mellem en 8-bit inputenhed og de 16-bit tegn i Java. Således, hvor som helst Java-strenge skulle læses fra eller skrives til en 8-bit stream, var der en lille smule kode, et hack, for magisk at kortlægge 8 bit-tegn i 16 bit unicode.

I 1.0 versionerne af Java Developer Kit (JDK) var input hacket i DataInputStream klasse, og outputhacket var hele PrintStream klasse. (Faktisk var der en inputklasse navngivet TextInputStream i alfa 2-frigivelsen af ​​Java, men den blev fortrængt af DataInputStream hack i den aktuelle udgivelse.) Dette medfører fortsat problemer for begyndende Java-programmører, da de desperat søger efter Java-ækvivalenten til C-funktionen getc (). Overvej følgende Java 1.0-program:

import java.io. *; public class falske {public static void main (String args []) {FileInputStream fis; DataInputStream dis; char c; prøv {fis = ny FileInputStream ("data.txt"); dis = ny DataInputStream (fis); mens (sand) {c = dis.readChar (); System.out.print (c); System.out.flush (); hvis (c == '\ n') bryde; } fis.close (); } fange (Undtagelse e) {} System.exit (0); }} 

Ved første øjekast ser dette program ud til at åbne en fil, læse den et tegn ad gangen og afslutte, når den første nye linje læses. Men i praksis er det, du får, uønsket output. Og grunden til at du får uønsket er det readChar læser 16-bit Unicode-tegn og System.out.print udskriver, hvad det antager er ISO Latin-1 8-bit tegn. Men hvis du ændrer ovenstående program for at bruge readLine funktion af DataInputStream, ser det ud til at fungere, fordi koden i readLine læser et format, der er defineret med en forbipasserende nik til Unicode-specifikationen som "modificeret UTF-8." (UTF-8 er det format, som Unicode angiver til at repræsentere Unicode-tegn i en 8-bit-inputstrøm.) Situationen i Java 1.0 er altså, at Java-strenge er sammensat af 16-bit Unicode-tegn, men der er kun en kortlægning, der kortlægger ISO Latin-1 tegn til Unicode. Heldigvis definerer Unicode kodesiden "0" - det vil sige de 256 tegn, hvis øvre 8 bit er nul - svarer nøjagtigt til ISO Latin-1-sættet. Kortlægningen er således temmelig triviel, og så længe du kun bruger ISO Latin-1-tegnfiler, har du ingen problemer, når dataene efterlader en fil, manipuleres af en Java-klasse og derefter omskrives til en fil .

Der var to problemer med at nedgrave inputkonverteringskoden i disse klasser: Ikke alle platforme lagrede deres flersprogede filer i ændret UTF-8-format; og bestemt forventede applikationerne på disse platforme ikke nødvendigvis ikke-latinske tegn i denne form. Derfor var implementeringsstøtten ufuldstændig, og der var ingen nem måde at tilføje den nødvendige support til i en senere udgivelse.

Java 1.1 og Unicode

Java 1.1-udgivelsen introducerede et helt nyt sæt grænseflader til håndtering af tegn, kaldet Læsere og Forfattere. Jeg ændrede den valgte klasse falske ovenfra til en klasse navngivet fedt nok. Det fedt nok klasse bruger en InputStreamReader klasse til at behandle filen i stedet for DataInputStream klasse. Noter det InputStreamReader er en underklasse af den nye Læser klasse og System.out er nu en PrintWriter objekt, som er en underklasse af Forfatter klasse. Koden til dette eksempel er vist nedenfor:

import java.io. *; public class cool {public static void main (String args []) {FileInputStream fis; InputStreamReader irs; char c; prøv {fis = ny FileInputStream ("data.txt"); irs = ny InputStreamReader (fis); System.out.println ("Brug af kodning:" + irs.getEncoding ()); while (true) {c = (char) irs.read (); System.out.print (c); System.out.flush (); hvis (c == '\ n') bryde; } fis.close (); } fange (Undtagelse e) {} System.exit (0); }} 

Den primære forskel mellem dette eksempel og den forrige kodeliste er brugen af InputStreamReader klasse snarere end DataInputStream klasse. En anden måde, hvorpå dette eksempel adskiller sig fra det foregående, er at der er en ekstra linje, der udskriver kodningen, der bruges af InputStreamReader klasse.

Det vigtige punkt er, at den eksisterende kode, når den ikke er dokumenteret (og tilsyneladende ukendt) og indlejret i implementeringen af getChar metode til DataInputStream klasse, er blevet fjernet (faktisk er brugen af ​​den forældet; den fjernes i en fremtidig udgivelse). I 1.1-versionen af ​​Java er den mekanisme, der udfører konverteringen, nu indkapslet i Læser klasse. Denne indkapsling giver Java-klassebibliotekerne mulighed for at understøtte mange forskellige eksterne repræsentationer af ikke-latinske tegn, mens de altid bruger Unicode internt.

Som det originale I / O-undersystemdesign er der selvfølgelig symmetriske modstykker til læseklasser, der udfører skrivning. Klassen OutputStreamWriter kan bruges til at skrive strenge til en outputstrøm, klassen BufferedWriter tilføjer et lag buffering og så videre.

Handelsvorter eller reel fremgang?

Det noget ophøjede mål med designet af Læser og Forfatterklasser var at temme det, der i øjeblikket er en hodge-podge af repræsentationsstandarder for de samme oplysninger ved at give en standard måde at konvertere frem og tilbage mellem den ældre repræsentation - det være sig Macintosh græsk eller Windows Cyrillic - og Unicode. Så en Java-klasse, der beskæftiger sig med strenge, behøver ikke at ændre sig, når den flytter fra platform til platform. Dette kan være slutningen på historien, bortset fra at nu konverteringskoden er indkapslet, opstår spørgsmålet om, hvad den kode antager.

Mens jeg undersøgte denne kolonne, blev jeg mindet om et berømt citat fra en Xerox-direktør (før det var Xerox, da det var Haloid Company) om, at fotokopimaskinen var overflødig, fordi det var ret let for en sekretær at lægge et stykke carbonpapir i hendes skrivemaskine og lav en kopi af et dokument, mens hun oprettede originalen. Selvfølgelig er det, der er åbenlyst i bakspejlet, at fotokopimaskinen gavner den person, der modtager et dokument, meget mere end den, der genererer et dokument. JavaSoft har vist en lignende mangel på indsigt i brugen af ​​karakterkodning og afkodningsklasser i deres design af denne del af systemet.