I set di caratteri | NEXT chapters | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Il problema del set di caratteri è una delle bestie nere della programmazione, soprattutto quando si mettono in comunicazione programmi o computer di differenti aree geografiche. In questo articolo, cercherò di dare un'idea generale di cosa siano un set di caratteri ed un "encoding" e di quali rapporti ci siano ( se ci sono ) tra un encoding e l'altro. Set di caratteri Encoding Bit American Standard Code for Information Interchange: codice standard americano per scambio di informazioni. Codice binario, ideato da Robert W. Bemer nel 1965 e approvato nel 1968 dall'Istituto Nazionale Americano per gli Standard ( ANSI ). Ai primordi dell'informatica, i computer parlavano solo americano. Il codice ASCII, quindi, doveva rappresentare il set di caratteri USA: caratteri numerici ( 0123456789 ), alfabetici maiuscoli e minuscoli, punteggiatura, simboli grafici e 32 codici per il controllo di periferiche e apparecchi di comunicazione ( caratteri non stampabili ). Pur avendo a disposizione 8 bit ( 1 byte ), cioè 256 codifiche differenti, ANSI stabilì i codici di 128 caratteri in totale, che allora sembravano più che sufficienti! In sostanza, ASCII utilizza solo 7 degli 8 bit a disposizione, lasciando il bit più significativo ( quello a sinistra ) sempre a zero.
Questa tabella divenne così uno standard, codificata, nel 1991, anche da ISO ( standard ISO Di fronte al proliferare di codifiche proprietarie, ISO decise di intervenire. Nel 1998 nacque lo standard ISO 8859. Lo standard ISO 8859 altro non è che un'estensione della tabella ASCII, alla quale vengono aggiunti i 128 codici ottenibili con l'utilizzo dell'ottavo bit. Lo scopo di ISO 8859 era: rappresentare i simboli grafici dei caratteri propri delle lingue europee, tanto è vero che ISO 8859 fu creato da European Computer Manufacturer's Association ( ECMA ) e solo successivamente ricevette l'appoggio di ISO. Ben presto ci si rese conto che rappresentare tutte le varianti possibili contenute nei diversi set di carattere europei con l'aggiunta di un solo bit ( avendo quindi a disposizione 128 codifiche ), non era realizzabile. Si decise, quindi, di istituzionalizzare e standardizzare tabelle di caratteri differenti per ciascuna area geografica che condividesse un set di caratteri, comprese aree geografiche extraeuropee. Nacquero così differenti versioni dello standard ISO 8859:
Alla pagina di Wikipedia
trovate l'elenco delle ISO 8859 accompagnate dall'elenco delle lingue supportate, oltre ad una tabella comparativa dei codici compresi tra il 160 ed il 255 con le loro differenti rappresentazioni grafiche. Proprio quest'ultima tabella comparativa, ci rivela che ISO 8859 istituzionalizzò l'incompatibilità tra una tabella e l'altra, incompatibilità che derivava necessariamente dalla limitazione dei bit disponibili e dal numero dei caratteri da rappresentare, ma certamente limitò il proliferare di decine e centinaia di versioni differenti e proprietarie che sarebbero rimaste inaccessibili alla stragrande maggioranza delle persone. Con ISO 8859, per scrivere ed inviare un documento testuale diventava necessario scegliere il set di caratteri da utilizzare per scrivere il documento ed informare il destinatario del set di caratteri utilizzato, affinchè lo installasse nella sua macchina per poter interpretare correttamente il documento. D'altronde, è bene ricordare che con una sequenza data di bit, per esempio:
si può rappresentare un solo simbolo grafico, non due! Ancora oggi, se dobbiamo aprire e leggere un documento codificato con una delle varianti ISO 8859, dobbiamo sapere quale delle varianti è stata utilizzata originariamente. Con la rapida diffusione dell'uso dei computer in tutto il mondo, ci si rese presto conto che si doveva inventare una qualche forma di encoding universale e, soprattutto, con corrispondenze codice
ma i documenti ECMA, approvati da ISO, sono reperibili gratuitamente all'interno del sito ECMA:
Lo standard internazionale ISO 10646 ( ISO
Abbiamo detto in precedenza che il solo modo di rappresentare, all'interno di un set di caratteri, più di 256 caratteri è di utilizzare unità di bit maggiori di 8. Visto che Unicode può rappresentare 1.114.112 caratteri, quanti bit utilizza? Inizialmente, Unicode utilizzava, per rappresentare i caratteri, unità a grandezza fissa: 16 bit. Quindi, Unicode, inizialmente, aveva code unit a 16 bit e poteva rappresentare 65536 caratteri, avendo a disposizione 65536 code point.
I primi 128 code point assegnati mantennero le stesse corrispondenze code point
nello standard Unicode venne assegnata allo stesso code point, ma rappresentata da una code unit a 16 bit ( 0041 in notazione esadecimale ) e non più a 8 bit:
Ricordate questa distinzione: Unicode utilizzava due byte ( 16 bit ) ed una code unit, avendo definito la code unit di Unicode come composta da due byte. Per quanto riguarda i code point dal 128 al 255 ( in notazione decimale ), code point non standardizzati nella codifica ASCII a 7 bit, ma standardizzati con differenti tabelle dallo standard ISO 8859, fu necessario scegliere una delle differenti codifiche ISO 8859, visto che ciascuna versione dello standard ISO 8859 assegnava ad uno stesso code point un differente carattere. La scelta cadde su ISO
code point in più! Questi nuovi code point vanno aggiunti ai 65.536 code point che scaturiscono dall'utilizzo di una code unit a 16 bit:
Grazie alle coppie di surrogati, quindi, l'intervallo numerico utilizzabile da Unicode per rappresentare i caratteri diventa:
di cui 2048 code point non potranno mai essere utilizzati per codificare un carattere, perchè destinati a rappresentare i code point surrogati. A questo punto, dobbiamo chiederci se l'utilizzo di code point surrogati sia il solo sistema per poter rappresentare tutti i code point di questo intervallo numerico:
La risposta, ovviamente è no! Unicode ha sviluppato tre forme di rappresentazione ( Encoding Forms ) dei code point che utilizzano tre differenti code unit. Nello standard Unicode i modelli di codifica, definiti encoding forms, specificano come un numero intero ( code point ) destinato a rappresentare un carattere Unicode, sia da esprimere come una sequenza di una o più code unit. Nel nostro caso, ci sarà una serie di regole da rispettare per far sì che due code point surrogati, ciacuno composto da una sola code unit a 16 bit, messi uno a fianco dell'altro ed opportunamente elaborati ( vedremo in seguito come ) diano come risultato un nuovo code point valido. Ad esempio, il code point U+10302 ( 66306 in notazione decimale ), normalmente non rappresentabile con 16 bit, può essere rappresentato utilizzando ed elaborando i seguenti due code point surrogati:
Le tre encoding forms attualmente implementate da Unicode sono chiamate UTF ( Unicode Transformation Format oppure UCS Transformation Format ). Ciascuna di queste tre encoding forms è da considerarsi un meccanismo legittimo per rappresentare i caratteri Unicode; ognuna di esse ha i propri vantaggi in contesti differenti. Ciascuna delle tre encoding forms di Unicode può essere trasformata in ciascuna delle due rimanenti in modo efficiente e senza alcuna perdita di dati. Le tre encoding forms previste da Unicode sono:
UTF-32 è la più semplice encoding form di Unicode. Ciascun code point è rappresentato direttamente da una sola code unit a 32 bit. Per questo, possiamo affermare che UTF-32 è una encoding form a grandezza fissa ( 32 bit ) e che ha corrispondenze uno-a-uno tra carattere codificato e code unit. Come tutte le encoding form di Unicode, UTF-32 si limita a rappresentare i code point compresi nell'intervallo numerico 0 .. 10FFFF ( in esadecimale ), che è il codespace di Unicode. Questo per garantire la interoperabilità con gli altri due encoding forms: UTF-16 e UTF-8.
Questo comporta che in UTF-32 il byte più significativo avrà sempre tutti i bit a zero, perchè l'insieme dei numeri rappresentabili con 32 bit è molto più grande dell'insieme che coincide con il codespace di Unicode. In UTF-32, il valore di ciascuna code unit coincide esattamente con il corrispondente code point Unicode ( perchè la code unit di UTF-32 è a 32 bit, coincidente con la code unit utilizzata dalla maggior parte dei sistemi per rappresentare i numeri ), mentre, come vedremo tra poco, in UTF-16 e UTF-8 il valore di una o più code unit può rendere irriconoscibile il valore del code point corrispondente. Per esempio, ecco come viene rappresentato il code point U+10000 in UTF-32 e in UTF-8 ( dove la code unit è a 8 bit, generando quindi 4 code unit per rappresentare il code point ):
Con UTF-32 è semplice determinare un carattere Unicode partendo dalla sua rappresentazione come code unit. Al contrario, le codifiche UTF-16 e UTF-8 spesso richiedono una conversione delle code unit prima di poter identificare il carattere Unicode rappresentato. UTF-32 può essere preferibile là dove lo spazio disco e la memoria non rappresentano un problema ( perchè UTF-32 utilizza 32 bit anche per rappresentare il code point U+01 ), e dove sia auspicabile l'uso di una singola code unit a grandezza fissa per accedere ai caratteri, come ad esempio accade su molte piattaforme Unix. UTF-16 è il diretto discendente del primo Unicode, quello originariamente strutturato ad utilizzare esclusivamente una encoding form a grandezza fissa ( 16 bit ). Come abbiamo visto in precedenza, nell'encoding form UTF-16 i code point che cadono nell'intervallo che va da U+0000 fino a U+FFFF ( 65535 in annotazione decimale ) vengono rappresentati come una singola code unit a 16 bit. I code point posti nell'intervallo compreso tra U+10000 ( 65536 in annotazione decimale ) e U+10FFFF ( 1.114.111 in annotazione decimale ), sono rappresentati come coppie di code point a 16 bit ( i surrogati ). I 2048 code point surrogati, che troviamo all'interno dell'intervallo numerico U+0000 ... U+FFFF, non saranno mai assegnati da Unicode ad un carattere, proprio perchè ciascuno di essi può assumere significato solo se abbinato ad un altro code point surrogato. Quindi, i code point assegnabili ad un carattere come singole code unit a 16 bit ( definiti, nello standard Unicode, Unicode scalar value ) sono, in realtà:
Per questi 63488 code point di UTF-16 valgono le stesse considerazioni fatte per UTF-32: il valore di ciascuna code unit coincide esattamente con il corrispondente code point Unicode ( perchè la code unit di UTF-16 è a 16 bit, coincidente con la code unit utilizzata dalla maggior parte dei sistemi per rappresentare i numeri fino a 65535 ). Quindi, per tutti i code point di UTF-16 compresi nell'intervallo numerico 0 ... 65535 non ci sarà bisogno di alcuna conversione. Bisogna aggiungere che, in media, più del 99 percento di tutti i code point UTF-16 assegnati sono espressi in singole code unit ( a 16 bit ). Come conseguenza, la gran parte delle operazioni di scansione del testo non richiederanno alcuna conversione di coppie di surrogati. Per molte operazioni, quindi, UTF-16 è semplice da gestire quanto UTF-32. Inoltre, lavorando con code unit a 16 bit, UTF-16 ha bisogno della metà della memoria richiesta da UTF-32. Di converso, la distinzione tra caratteri rappresentati con una sola code unit a 16 bit e caratteri rappresentati da coppie di code unit a 16 bit, rende UTF-16 una encoding form a dimensione variabile. Questo fatto può creare alcune difficoltà nelle implementazioni, se non se ne tiene conto con cura. UTF-16 è talvolta più complicato da gestire di UTF-32. La distribuzione dei bit per l'encoding form di UTF-16 varia a seconda che il code point sia compreso nell'intervallo numerico 0 ... 65535 piuttosto che nell'intervallo superiore: 65536 ... 1.114.111. Nel primo caso, abbiamo:
che esprime perfetta identità tra code point e code unit. Quindi, il code point Unicode U+0430 ( 1072 in notazione decimale ) sarà espresso, in UTF-16, dalla code unit U+0430:
Nel secondo caso, invece, deve intervenire una trasformazione:
In questo esempio, vediamo un modello di un code point:
decomposto nella coppia corrispondente di code point surrogati, secondo l'encoding form UTF-16:
Noterete che il risultato è composto da due code unit a 16 bit. Perchè il code point originario viene rappresentato come un byte ( 8 bit ) + una word ( 16 bit )? La spiegazione è semplice: il valore massimo che un code point Unicode può assumere è: U+10FFFF ( 1.114.111 in annotazione decimale ), quindi:
Questa quantità è quindi rappresentabile con soli 3 byte, di cui il più significativo ha sempre i tre bit più significativi a zero! Solo i bit dal ventunesimo al primo possono assumere valori differenti. Quindi, seguendo il modello di trasformazione UTF-16, abbiamo che:
Seguiamo ora un altro esempio, per verificare se la nostra comprensione è corretta. Code point: U+10302 ( 66306 in notazione decimale ):
Leading surrogate:
Trailing surrogate:
Quindi, secondo UTF-16, il code point U+10302 è rappresentato dalla seguente sequenza di due code unit a 16 bit ( leading surrogate + trailing surrogate ):
A questo punto, dovrebbe essere chiaro il compito affidato dallo standard Unicode ai code point surrogati. Visto che Unicode dovrebbe essere codificato a 16 bit, qualsiasi programma che catturi il flusso di bit in arrivo per poi interpretarlo, suddividerà il flusso di bit in gruppi di 16 bit. Quindi, il leading surrogate dovrà contenere, oltre ad una parte del reale code point da rappresentare, anche un "avviso" destinato al programma di decodifica: attenzione: questa code unit a 16 bit NON è un carattere, ma deve essere interpretata e decodificata insieme alla code unit a 16 bit in arrivo subito dopo questa! L'avviso, in UTF-16, è rappresentato dal gruppo di cinque bit più significativi ( 110110 ) contenuti nel leading surrogate. Ora vedremo che un meccanismo simile viene utilizzato anche nella terza encoding form di Unicode: UTF-8 ( code unit a 8 bit ). I due encoding form visti precedentemente, UTF-32 e UTF-16, pongono una serie di problemi quando si ha a che fare con sistemi fondati su code unit a 8 bit. La semplice decodifica di caratteri pone il problema di avere molti testi scritti in ASCII, che ha code unit a 8 bit, mentre la code unit minima di Unicode è a 16 bit. L'encoding form UTF-8 offre, al contrario, una piena compatibilità con i sistemi a 8 bit, proprio perchè assume una code unit a 8 bit. Naturalmente, stiamo parlando di piena compatibilità con ASCII a 7 bit, la versione standardizzata. Infatti, come per UTF-16, anche in UTF-8 almeno un bit deve essere utilizzato per attivare gli eventuali meccanismi di trasformazione. Ora siamo in grado di verificare le differenze di rappresentazione tra un encoding e l'altro. Prendiamo ad esempio un code point presente nell'intervallo U+0000 ... U+007F ( 0 ... 127 in annotazione decimale ), affinchè si possa inserire nel confronto anche ASCII a 7 bit: 4D ( lettera M ):
UTF-8 è una encoding form a grandezza variabile che assegna un code point ( o carattere o Unicode scalar value ) ad una sequenza di byte ( o code unit, che in UTF-8 sono a 8 bit ) composta da un numero variabile di byte ( da uno a quattro ), in cui i bit più significativi di ciascun byte ( quindi, di ciascuna code unit ) indicano il posto occupato dal byte ( quindi dalla code unit ) all'interno della sequenza stessa. Se la grandezza della sequenza è variabile ( da uno a quattro byte o code unit ), in UTF-8 avremo un leading element e da uno a tre trailing element. In UTF-8 non vengono utilizzati i code point surrogati, che sono una prerogativa esclusiva del modello di trasformazione ( o di distribuzione dei bit ) di UTF-16. UTF-8 prevede 4 differenti modelli di trasformazione ( o di distribuzione dei bit ). Nel considerare questi quattro modelli, ricordate che stiamo parlando di modelli di trasformazione Unicode, cioè di uno standard che era nato a 16 bit, ma che oggi deve rappresentare un intervallo numerico compreso tra U+0000 e U+10FFFF.
Vediamo ora in dettaglio i quattro modelli di trasformazione ( o di ditribuzione dei bit ) di UTF-8.
Il concetto fondamentale da assimilare è la relazione esistente tra Unicode e UTF-8, UTF-16, UTF-32: Unicode è il set di caratteri codificati, dove ogni carattere ( o metacarattere ) è assegnato ad un code point unico ( ci sono pochissime eccezioni dovute a problemi di compatibilità con versioni precedenti ), mentre UTF-8, UTF-16 e UTF-32 sono tre encoding forms, vale a dire tre forme differenti di distribuzione dei bit studiate per rappresentare tutti i code point di Unicode. Sottolineo questo aspetto perchè spesso si sente parlare di UTF-8 e Unicode come due set di caratteri differenti, mentre Unicode è semplicemente una tabella contenente le corrispondenze tra singoli code point e singoli caratteri, mentre UTF-8 ( come UTF-16 e UTF-32 ) è una modalità di rappresentazione di tutti i code point contenuti nella tabella Unicode. Per conoscere, quindi, la codifica di un determinato carattere in Unicode, bisogna prima di tutto recuperare la tabella Unicode e scovare il carattere ricercato ( a volte, questa semplice ricerca si rivela davvero ardua ), poi decidere in quale formato rappresentarlo: UTF-8, UTF-16 o UTF-32. Il luogo più sicuro dove trovare il code point di un carattere Unicode, è il sito dell'Unicode Consortium, alla pagina:
Per avere, invece, una tabella delle corrispondenze tra code point Unicode e rappresentazione UTF-8, seguire il link: |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
I set di caratteri | The .bit guides: original contents |