Προγραμματισμός

Μια σε βάθος ματιά στον τύπο χαρακτήρων της Java

Η έκδοση 1.1 της Java εισάγει μια σειρά μαθημάτων για την αντιμετώπιση χαρακτήρων. Αυτές οι νέες τάξεις δημιουργούν μια αφαίρεση για τη μετατροπή από μια έννοια πλατφόρμας για τις τιμές χαρακτήρων σε Unicode αξίες. Αυτή η στήλη εξετάζει τι έχει προστεθεί και τα κίνητρα για την προσθήκη αυτών των κατηγοριών χαρακτήρων.

Τύπος απανθρακώνω

Ίσως ο πιο βασικός τύπος βάσης στη γλώσσα C είναι ο τύπος απανθρακώνω. ο απανθρακώνω Ο τύπος έχει καταχραστεί εν μέρει επειδή ορίζεται ως 8 bit, και για τα τελευταία 25 χρόνια, τα 8 bit έχουν επίσης ορίσει το μικρότερο αδιαίρετο κομμάτι μνήμης σε υπολογιστές. Όταν συνδυάζετε το τελευταίο γεγονός με το γεγονός ότι το σύνολο χαρακτήρων ASCII ορίστηκε για να χωρά σε 7 bit, το απανθρακώνω τύπος κάνει έναν πολύ βολικό "καθολικό" τύπο. Επιπλέον, στο C, ένας δείκτης προς μια μεταβλητή τύπου απανθρακώνω έγινε ο καθολικός τύπος δείκτη επειδή οτιδήποτε θα μπορούσε να αναφέρεται ως απανθρακώνω θα μπορούσε επίσης να αναφέρεται ως οποιοσδήποτε άλλος τύπος μέσω της χρήσης χύτευσης.

Η χρήση και κατάχρηση του απανθρακώνω Ο τύπος στη γλώσσα C οδήγησε σε πολλές ασυμβατότητες μεταξύ των εφαρμογών μεταγλωττιστή, οπότε στο πρότυπο ANSI για το C, έγιναν δύο συγκεκριμένες αλλαγές: Ο γενικός δείκτης επαναπροσδιορίστηκε για να έχει έναν τύπο κενού, απαιτώντας έτσι μια ρητή δήλωση από τον προγραμματιστή. και η αριθμητική τιμή των χαρακτήρων θεωρήθηκε υπογεγραμμένη, ορίζοντας έτσι τον τρόπο αντιμετώπισής τους όταν χρησιμοποιούνται σε αριθμητικούς υπολογισμούς. Στη συνέχεια, στα μέσα της δεκαετίας του 1980, μηχανικοί και χρήστες διαπίστωσαν ότι τα 8 bit ήταν ανεπαρκή για να αντιπροσωπεύσουν όλους τους χαρακτήρες στον κόσμο. Δυστυχώς, εκείνη την εποχή, ο Γ ήταν τόσο εδραιωμένος που οι άνθρωποι δεν ήταν πρόθυμοι, ίσως ακόμη και ανίκανοι, να αλλάξουν τον ορισμό του απανθρακώνω τύπος. Τώρα προχωρήστε προς τα εμπρός στη δεκαετία του '90, στις αρχές της Java. Μία από τις πολλές αρχές που καθορίζονται στο σχεδιασμό της γλώσσας Java ήταν ότι οι χαρακτήρες θα είναι 16 bits. Αυτή η επιλογή υποστηρίζει τη χρήση του Unicode, ένας τυπικός τρόπος αναπαραγωγής πολλών διαφορετικών ειδών χαρακτήρων σε πολλές διαφορετικές γλώσσες. Δυστυχώς, έθεσε επίσης το στάδιο για μια ποικιλία προβλημάτων που τώρα διορθώνονται.

Τι είναι ο χαρακτήρας ούτως ή άλλως;

Ήξερα ότι ήμουν σε μπελάδες όταν βρέθηκα να θέσω την ερώτηση: είναι ένας χαρακτήρας; "Λοιπόν, ένας χαρακτήρας είναι ένα γράμμα, σωστά; Ένα σωρό γράμματα συνθέτουν μια λέξη, λέξεις σχηματίζουν προτάσεις και ούτω καθεξής. Η πραγματικότητα, ωστόσο, είναι ότι η σχέση μεταξύ της αναπαράστασης ενός χαρακτήρα σε μια οθόνη υπολογιστή , ονομάζεται γλύφος, στην αριθμητική τιμή που καθορίζει το glyph, που ονομάζεται a σημείο κώδικα, δεν είναι καθόλου απλό.

Θεωρώ τον εαυτό μου τυχερό που είμαι ομιλητής της αγγλικής γλώσσας. Πρώτον, επειδή ήταν η κοινή γλώσσα ενός σημαντικού αριθμού εκείνων που συνέβαλαν στο σχεδιασμό και την ανάπτυξη του σύγχρονου ψηφιακού υπολογιστή. Δεύτερον, επειδή έχει σχετικά μικρό αριθμό γλύφων. Υπάρχουν 96 εκτυπώσιμοι χαρακτήρες στον ορισμό ASCII που μπορούν να χρησιμοποιηθούν για τη σύνταξη αγγλικών. Συγκρίνετε αυτό με τα κινέζικα, όπου έχουν οριστεί πάνω από 20.000 γλύφοι και αυτός ο ορισμός είναι ελλιπής. Από τις αρχές του κώδικα Morse και Baudot, η συνολική απλότητα (λίγα γλύφοι, στατιστική συχνότητα εμφάνισης) της αγγλικής γλώσσας το καθιστά το lingua-franca της ψηφιακής εποχής. Όμως, καθώς ο αριθμός των ατόμων που εισέρχονται στην ψηφιακή εποχή έχει αυξηθεί, έτσι και ο αριθμός των μη γηγενών αγγλόφωνων. Καθώς οι αριθμοί αυξάνονταν, όλο και περισσότεροι άνθρωποι απείχαν όλο και περισσότερο να δεχτούν ότι οι υπολογιστές χρησιμοποιούσαν ASCII και μιλούσαν μόνο αγγλικά. Αυτό αύξησε σημαντικά τον αριθμό των "χαρακτήρων" υπολογιστών που πρέπει να κατανοήσουν. Ως αποτέλεσμα, ο αριθμός των γλύφων που κωδικοποιήθηκαν από υπολογιστές έπρεπε να διπλασιαστεί.

Ο αριθμός των διαθέσιμων χαρακτήρων διπλασιάστηκε όταν ο σεβάσμιος κώδικας ASCII 7-bit ενσωματώθηκε σε κωδικοποίηση χαρακτήρων 8-bit που ονομάζεται ISO Latin-1 (ή ISO 8859_1, με το "ISO" να είναι ο Διεθνής Οργανισμός Προτύπων). Όπως ίσως έχετε συγκεντρώσει το όνομα κωδικοποίησης, αυτό το πρότυπο επέτρεπε την αναπαράσταση πολλών από τις λατινικές γλώσσες που προέρχονται από την ευρωπαϊκή ήπειρο. Ακριβώς επειδή το πρότυπο δημιουργήθηκε, ωστόσο, δεν σήμαινε ότι μπορεί να χρησιμοποιηθεί. Εκείνη την εποχή, πολλοί υπολογιστές είχαν ήδη αρχίσει να χρησιμοποιούν τους άλλους 128 "χαρακτήρες" που θα μπορούσαν να αντιπροσωπεύονται από έναν χαρακτήρα 8-bit για κάποιο πλεονέκτημα. Τα δύο σωστά παραδείγματα χρήσης αυτών των επιπλέον χαρακτήρων είναι ο IBM Personal Computer (PC) και το πιο δημοφιλές τερματικό υπολογιστή που έχει γίνει ποτέ, το Digital Equipment Corporation VT-100. Το τελευταίο ζει με τη μορφή λογισμικού τερματικού εξομοιωτή.

Ο πραγματικός χρόνος του θανάτου για τον χαρακτήρα 8-bit αναμφίβολα θα συζητηθεί για δεκαετίες, αλλά το συνδέω με την εισαγωγή του υπολογιστή Macintosh το 1984. Το Macintosh έφερε δύο πολύ επαναστατικές έννοιες στον mainstream υπολογισμό: γραμματοσειρές χαρακτήρων που ήταν αποθηκευμένες σε ΕΜΒΟΛΟ; και WorldScript, τα οποία θα μπορούσαν να χρησιμοποιηθούν για την αναπαράσταση χαρακτήρων σε οποιαδήποτε γλώσσα. Φυσικά, αυτό ήταν απλώς ένα αντίγραφο αυτού που έστειλε η Xerox στα μηχανήματα της κατηγορίας Dandelion με τη μορφή του συστήματος επεξεργασίας κειμένου Star, αλλά το Macintosh έφερε αυτά τα νέα σύνολα χαρακτήρων και γραμματοσειρές σε ένα κοινό που εξακολουθούσε να χρησιμοποιεί "χαζή" τερματικά . Μόλις ξεκίνησε, η χρήση διαφορετικών γραμματοσειρών δεν μπορούσε να σταματήσει - ήταν πολύ ελκυστικό για πάρα πολλά άτομα. Στα τέλη της δεκαετίας του '80, η πίεση για τυποποίηση της χρήσης όλων αυτών των χαρακτήρων ήρθε στο μυαλό με το σχηματισμό της Unicode Consortium, η οποία δημοσίευσε την πρώτη της προδιαγραφή το 1990. Δυστυχώς, κατά τη διάρκεια της δεκαετίας του '80 και ακόμη και στη δεκαετία του '90, η πολλαπλασιασμένος αριθμός συνόλων χαρακτήρων. Πολύ λίγοι από τους μηχανικούς που δημιούργησαν νέους κωδικούς χαρακτήρων τότε θεωρούσαν βιώσιμο το πρότυπο Unicode, και έτσι δημιούργησαν τις δικές τους αντιστοιχίσεις κωδικών σε γλύφους. Έτσι, ενώ το Unicode δεν έγινε δεκτό, η ιδέα ότι υπήρχαν μόνο 128 ή το πολύ 256 χαρακτήρες ήταν σίγουρα χαμένη. Μετά το Macintosh, η υποστήριξη για διαφορετικές γραμματοσειρές έγινε απαραίτητο χαρακτηριστικό για την επεξεργασία κειμένου. Οκτώ χαρακτήρες bit εξαφανίστηκαν.

Java και Unicode

Μπήκα στην ιστορία το 1992 όταν μπήκα στην ομάδα Oak (Η γλώσσα Java ονομάστηκε Oak όταν αναπτύχθηκε για πρώτη φορά) στο Sun. Ο τύπος βάσης απανθρακώνω ορίστηκε ως 16 μη υπογεγραμμένα bits, ο μόνος μη υπογεγραμμένος τύπος στην Java. Το σκεπτικό για τον χαρακτήρα 16-bit ήταν ότι θα υποστηρίζει οποιαδήποτε αναπαράσταση χαρακτήρων Unicode, καθιστώντας έτσι την Java κατάλληλη για την αναπαράσταση συμβολοσειρών σε οποιαδήποτε γλώσσα υποστηρίζεται από το Unicode. Όμως, η δυνατότητα αναπαραγωγής της συμβολοσειράς και η εκτύπωσή της ήταν πάντα ξεχωριστά προβλήματα. Δεδομένου ότι το μεγαλύτερο μέρος της εμπειρίας στην ομάδα Oak προήλθε από συστήματα Unix και συστήματα που προέρχονται από Unix, το πιο άνετο σύνολο χαρακτήρων ήταν, πάλι, το ISO Latin-1. Επίσης, με την κληρονομιά της ομάδας Unix, το σύστημα Java I / O μοντελοποιήθηκε σε μεγάλο βαθμό στην αφαίρεση ροής Unix, όπου κάθε συσκευή I / O θα μπορούσε να αντιπροσωπεύεται από μια ροή byte 8-bit. Αυτός ο συνδυασμός άφησε κάτι λάθος στη γλώσσα μεταξύ μιας συσκευής εισόδου 8-bit και των χαρακτήρων 16-bit της Java. Έτσι, οπουδήποτε έπρεπε να διαβαστούν ή να γραφτούν οι συμβολοσειρές Java σε μια ροή 8-bit, υπήρχε ένα μικρό κομμάτι κώδικα, ένα hack, για να χαρτογραφήσει μαγικά τους χαρακτήρες 8 bit σε unicode 16 bit.

Στις εκδόσεις 1.0 του Java Developer Kit (JDK), η είσοδος εισόδου ήταν στο DataInputStream τάξη και το hack εξόδου ήταν το σύνολο PrintStream τάξη. (Στην πραγματικότητα υπήρχε μια κατηγορία εισαγωγής με το όνομα TextInputStream στην έκδοση alpha 2 της Java, αλλά αντικαταστάθηκε από το DataInputStream hack στην πραγματική έκδοση.) Αυτό συνεχίζει να προκαλεί προβλήματα στην έναρξη προγραμματιστών Java, καθώς αναζητούν απεγνωσμένα το ισοδύναμο Java της συνάρτησης C πάρει (). Εξετάστε το ακόλουθο πρόγραμμα Java 1.0:

εισαγωγή java.io. *; ψεύτικη δημόσια τάξη {public static void main (String args []) {FileInputStream fis; DataInputStream dis; char γ; δοκιμάστε το {fis = new FileInputStream ("data.txt"); dis = νέο DataInputStream (fis); ενώ (true) {c = dis.readChar (); System.out.print (γ); System.out.flush (); εάν (c == '\ n') σπάσει; } fis.close (); } catch (Εξαίρεση e) {} System.exit (0); }} 

Με την πρώτη ματιά, αυτό το πρόγραμμα φαίνεται να ανοίγει ένα αρχείο, να το διαβάζει έναν χαρακτήρα κάθε φορά και να κλείνει όταν διαβάζεται η πρώτη νέα γραμμή. Ωστόσο, στην πράξη, αυτό που παίρνετε είναι ανεπιθύμητη παραγωγή. Και ο λόγος που παίρνετε σκουπίδια είναι αυτός διαβάστε το Char διαβάζει χαρακτήρες Unicode 16-bit και System.out.print εκτυπώνει ό, τι υποθέτει ότι είναι χαρακτήρες ISO-1 8-bit ISO. Ωστόσο, εάν αλλάξετε το παραπάνω πρόγραμμα για να χρησιμοποιήσετε το readLine λειτουργία του DataInputStream, φαίνεται να λειτουργεί επειδή ο κωδικός στο readLine διαβάζει μια μορφή που ορίζεται με ένα νεκρό πέρασμα στην προδιαγραφή Unicode ως "τροποποιημένο UTF-8." (Το UTF-8 είναι η μορφή που καθορίζει το Unicode για την αναπαράσταση χαρακτήρων Unicode σε ροή εισόδου 8-bit.) Έτσι, η κατάσταση στο Java 1.0 είναι ότι οι συμβολοσειρές Java αποτελούνται από χαρακτήρες Unicode 16-bit, αλλά υπάρχει μόνο μία αντιστοίχιση που χαρτογραφεί ISO Latin-1 χαρακτήρες στο Unicode. Ευτυχώς, το Unicode ορίζει τη σελίδα κώδικα "0" - δηλαδή, οι 256 χαρακτήρες των οποίων τα 8 bit είναι όλα μηδέν - για να αντιστοιχούν ακριβώς στο σετ ISO Latin-1. Επομένως, η αντιστοίχιση είναι αρκετά ασήμαντη και εφ 'όσον χρησιμοποιείτε μόνο αρχεία χαρακτήρων ISO Latin-1, δεν θα έχετε προβλήματα όταν τα δεδομένα αφήνουν ένα αρχείο, χειρίζονται από μια κλάση Java και, στη συνέχεια, ξαναγράφονται σε ένα αρχείο .

Υπήρχαν δύο προβλήματα με την ταφή του κωδικού μετατροπής εισόδου σε αυτές τις τάξεις: Δεν αποθηκεύτηκαν όλες οι πλατφόρμες τα πολύγλωσσα αρχεία τους σε τροποποιημένη μορφή UTF-8. και σίγουρα, οι εφαρμογές σε αυτές τις πλατφόρμες δεν περιμένουν απαραίτητα μη λατινικούς χαρακτήρες σε αυτήν τη μορφή. Επομένως, η υποστήριξη υλοποίησης ήταν ελλιπής και δεν υπήρχε εύκολος τρόπος να προσθέσετε την απαιτούμενη υποστήριξη σε μεταγενέστερη έκδοση.

Java 1.1 και Unicode

Η έκδοση Java 1.1 εισήγαγε ένα εντελώς νέο σύνολο διεπαφών για το χειρισμό χαρακτήρων, που ονομάζεται Αναγνώστες και Συγγραφείς. Τροποποίησα την τάξη με το όνομα ψευδής από ψηλά σε μια τάξη που ονομάζεται δροσερός. ο δροσερός η τάξη χρησιμοποιεί ένα InputStreamReader κλάση για την επεξεργασία του αρχείου και όχι του DataInputStream τάξη. Σημειώστε ότι InputStreamReader είναι μια υποκατηγορία του νέου Αναγνώστης τάξη και το System.out είναι τώρα ένα Εκτύπωση αντικείμενο, το οποίο είναι μια υποκατηγορία του Συγγραφέας τάξη. Ο κώδικας για αυτό το παράδειγμα εμφανίζεται παρακάτω:

εισαγωγή java.io. *; δημόσια τάξη cool {public static void main (String args []) {FileInputStream fis; InputStreamReader irs; char γ; δοκιμάστε το {fis = new FileInputStream ("data.txt"); irs = νέο InputStreamReader (fis); System.out.println ("Χρήση κωδικοποίησης:" + irs.getEncoding ()); ενώ (true) {c = (char) irs.read (); System.out.print (γ); System.out.flush (); εάν (c == '\ n') διακοπή; } fis.close (); } catch (Εξαίρεση e) {} System.exit (0); }} 

Η κύρια διαφορά μεταξύ αυτού του παραδείγματος και της προηγούμενης λίστας κώδικα είναι η χρήση του InputStreamReader τάξη και όχι το DataInputStream τάξη. Ένας άλλος τρόπος με τον οποίο αυτό το παράδειγμα είναι διαφορετικό από το προηγούμενο είναι ότι υπάρχει μια επιπλέον γραμμή που εκτυπώνει την κωδικοποίηση που χρησιμοποιείται από το InputStreamReader τάξη.

Το σημαντικό σημείο είναι ότι ο υφιστάμενος κώδικας, κάποτε χωρίς έγγραφα (και φαινομενικά άγνωστος) και ενσωματωμένος μέσα στην εφαρμογή του getChar μέθοδος του DataInputStream class, έχει καταργηθεί (στην πραγματικότητα η χρήση του έχει καταργηθεί, θα καταργηθεί σε μελλοντική έκδοση). Στην έκδοση 1.1 της Java, ο μηχανισμός που εκτελεί τη μετατροπή είναι πλέον ενσωματωμένος στο Αναγνώστης τάξη. Αυτή η ενθυλάκωση παρέχει έναν τρόπο για τις βιβλιοθήκες κλάσης Java να υποστηρίζουν πολλές διαφορετικές εξωτερικές αναπαραστάσεις μη λατινικών χαρακτήρων ενώ χρησιμοποιούν πάντα το Unicode εσωτερικά.

Φυσικά, όπως και ο αρχικός σχεδιασμός υποσυστήματος I / O, υπάρχουν συμμετρικά αντίστοιχα στις τάξεις ανάγνωσης που εκτελούν γραφή. Η τάξη OutputStreamWriter μπορεί να χρησιμοποιηθεί για την εγγραφή συμβολοσειρών σε μια ροή εξόδου, την κλάση BufferedWriter προσθέτει ένα επίπεδο buffering, και ούτω καθεξής.

Ανταλλάξτε κονδυλώματα ή πραγματική πρόοδο;

Ο κάπως υψηλός στόχος του σχεδιασμού του Αναγνώστης και ΣυγγραφέαςΤα μαθήματα ήταν να εξημερώσουν αυτό που είναι επί του παρόντος ένα hodge-podge των προτύπων αναπαράστασης για τις ίδιες πληροφορίες παρέχοντας έναν τυπικό τρόπο μετατροπής μεταξύ των παλαιών παραστάσεων - είτε πρόκειται για Macintosh Greek ή Windows Cyrillic - και Unicode. Έτσι, μια κλάση Java που ασχολείται με χορδές δεν χρειάζεται να αλλάζει όταν μετακινείται από πλατφόρμα σε πλατφόρμα. Αυτό μπορεί να είναι το τέλος της ιστορίας, με την εξαίρεση ότι τώρα που ο κώδικας μετατροπής είναι ενθυλακωμένος, τίθεται το ερώτημα τι υποθέτει αυτός ο κώδικας.

Κατά την έρευνα αυτής της στήλης, μου θύμισε ένα διάσημο απόσπασμα από ένα στέλεχος της Xerox (πριν ήταν η Xerox, όταν ήταν η εταιρεία Haloid) σχετικά με το περιττό φωτοτυπικό μηχάνημα επειδή ήταν αρκετά εύκολο για έναν γραμματέα να βάλει ένα κομμάτι χαρτιού άνθρακα τη γραφομηχανή της και να δημιουργήσει ένα αντίγραφο ενός εγγράφου ενώ δημιουργούσε το πρωτότυπο. Φυσικά, αυτό που είναι προφανές εκ των υστέρων είναι ότι η μηχανή φωτοαντιγράφων ωφελεί το άτομο που λαμβάνει ένα έγγραφο πολύ περισσότερο από ό, τι ένα άτομο που δημιουργεί ένα έγγραφο. Το JavaSoft έχει δείξει μια παρόμοια έλλειψη γνώσης σχετικά με τη χρήση των κλάσεων κωδικοποίησης και αποκωδικοποίησης χαρακτήρων στο σχεδιασμό αυτού του μέρους του συστήματος.