UTF-8: la codifica standard di caratteri in rete
Chiunque legga un sito web in inglese o un'e-mail in giapponese non solo parla entrambe le lingue, ma è molto probabilmente anche testimone del trionfo della codifica UTF-8. "UTF-8" è l'abbreviazione di "8-Bit UCS Transformation Format" e rappresenta la codifica di caratteri più diffusa nel World Wide Web. Lo standard internazionale Unicode copre tutti i caratteri linguistici e gli elementi di testo di (quasi) tutte le lingue del mondo per l'elaborazione EDP. La codifica UTF-8 gioca un ruolo fondamentale nel set di caratteri Unicode.
Evoluzione della codifica UTF-8
UTF-8 è una codifica di caratteri che assegna a ogni carattere Unicode esistente una specifica sequenza di bit, che può essere letta anche come numero binario. Questo significa che UTF-8 assegna un numero binario fisso a ogni lettera, numero e simbolo di un numero crescente di lingue. Le organizzazioni internazionali, che sono particolarmente interessate agli standard Internet e di conseguenza alla loro uniformazione, stanno lavorando per rendere UTF-8 il modello di codifica per eccellenza. Alcuni esempi sono il W3C e l’Internet Engineering Task Force. Già nel 2009 infatti, la maggior parte dei siti web nel mondo utilizzava la codifica UTF-8. Secondo un rapporto del W3Techs del marzo 2018, il 90,9% di tutti i siti web esistenti utilizza questa codifica di caratteri.
Problemi prima dell'introduzione di UTF-8
Regioni diverse con lingue e sistemi di scrittura imparentati avevano sviluppato ciascuna i propri standard di codifica per soddisfare esigenze diverse. Nei paesi anglofoni, ad esempio, era sufficiente la codifica ASCII, la cui struttura consente l'assegnazione di 128 caratteri a una sequenza di caratteri leggibile al computer. Le scritture asiatiche o l'alfabeto cirillico utilizzano, tuttavia, caratteri singoli più univoci. Anche le dieresi tedesche (come la lettera ä) mancano nell'ASCII.
Esisteva la possibilità che le assegnazioni di codifiche diverse si sovrapponessero. Questo significa che, ad esempio, un documento scritto in russo poteva apparire su un computer americano con le lettere latine assegnate a questo sistema invece che in lettere cirilliche. Risultati di questo genere hanno ovviamente reso la comunicazione internazionale molto più difficile.
Nascita di UTF-8
Per risolvere questo problema Joseph D. Becker sviluppò il set di caratteri universale Unicode per Xerox tra il 1988 e il 1991. Dal 1992 anche il consorzio industriale informatico X/Open era alla ricerca di un sistema per sostituire ASCII ed espandere il suo repertorio di caratteri. Un requisito importante era che tale codifica fosse compatibile con ASCII. Tuttavia la prima codifica, chiamata UCS-2, non soddisfaceva questo requisito e si limitava a convertire il valore numerico dei caratteri in valori a 16 bit.
L’obiettivo di compatibilità non fu raggiunto nemmeno dalla codifica UTF-1, dato che le assegnazioni Unicode si sovrapponevano in parte con quelle dei caratteri ASCII esistenti. Un server impostato su ASCII, pertanto, poteva facilmente produrre caratteri errati. Questo rappresentava un problema considerevole, dato che all'epoca la maggior parte dei computer anglofoni lavorava con questa codifica. Il tentativo successivo fu quello del File System Safe UCS Transformation Format (FSS-UTF) di Dave Prosser, che risolse il problema della sovrapposizione con i caratteri ASCII.
Nell'agosto dello stesso anno il progetto si diffuse tra gli esperti. All'epoca nei Bell Labs, noti per i numerosi premi Nobel, lavoravano al sistema operativo Plan 9 i cofondatori di Unix Ken Thompson e Rob Pike, che ripresero l'idea di Prosser, sviluppando una codifica in grado di auto-sincronizzarsi (ogni carattere indica di quanti bit ha bisogno) e stabilendo regole per l'assegnazione di caratteri che potevano essere rappresentati in modo diverso nel codice (esempio: "ä" come carattere proprio o "a+¨"). Hanno sfruttato questa codifica con successo per il loro sistema operativo, presentandola in seguito ai responsabili. Nacque così la codifica FSS-UTF, oggi nota come "UTF-8".
UTF-8 è una codifica di caratteri a 8 bit per Unicode. L'abbreviazione "UTF-8" sta per "8-Bit Universal Character Set Transformation Format", in italiano: "formato di conversione universale del set di caratteri a 8 bit". Da uno a quattro byte, ciascuno composto da otto bit, formano un numero binario leggibile al computer, che assegna la codifica a un carattere o a un altro elemento di testo. La struttura auto-sincronizzante e la possibilità di generare 221 numeri binari consente l'assegnazione univoca di ogni carattere ed elemento di testo di tutte le lingue del mondo.
UTF-8 nel set di caratteri Unicode: uno standard per tutte le lingue
La codifica UTF-8 è un formato di conversione appartenente allo standard Unicode. Il set di caratteri Unicode viene definito a grandi linee nello standard internazionale ISO 10646, che lo indica con il termine "Universal Coded Character Set". Per garantire più praticità nell'uso, gli sviluppatori dello standard hanno deciso di limitarne alcuni parametri. Lo standard ha lo scopo di garantire una codifica di caratteri ed elementi di testo uniforme e compatibile a livello internazionale. Quando fu introdotto, nel 1991, lo standard Unicode definì 24 moderni sistemi di scrittura e simboli di valuta per l'elaborazione dei dati. Nel giugno 2017 erano 139.
Esistono diversi formati di conversione Unicode, i cosiddetti "UTF", che riproducono gli 1.114.112 punti di codice possibili. I formati che hanno prevalso sono tre: UTF-8, UTF-16 e UTF-32. Anche altre codifiche come UTF-7 o SCSU hanno i loro vantaggi, ma non si sono mai affermate.
Unicode è diviso in 17 piani, ognuno dei quali contiene 65.536 caratteri. Ogni piano è composto da 16 colonne e 16 righe. Il primo piano, chiamato "Basic Multilingual Plane" (piano 0), copre la maggior parte dei sistemi di scrittura attualmente utilizzati nel mondo, così come i segni di punteggiatura, i caratteri di controllo e i simboli. Attualmente sono in uso altri cinque piani:
- Supplementary Multilingual Plane (piano 1): sistemi di scrittura antichi, caratteri usati raramente
- Supplementary Ideographic Plane (piano 2): caratteri ideografici rari CJK ("cinese, giapponese, coreano")
- Supplementary Special-Purpose Plane (piano 14): singoli caratteri di controllo
- Supplementary Private Use Area - A (piano 15): uso privato
- Supplementary Private Use Area - B (piano 16): uso privato
Le codifiche UTF consentono di accedere a tutti i caratteri Unicode. Grazie alle rispettive caratteristiche i singoli piani possono essere sfruttati per contesti diversi.
Le alternative: UTF-32 e UTF-16
UTF-32 utilizza sempre sequenze di numeri a 32 bit, ovvero 4 byte. La semplicità della sua struttura migliora la leggibilità del formato. Nelle lingue che utilizzano principalmente l'alfabeto latino e quindi solo i primi 128 caratteri, questa codifica occupa molta più memoria del necessario (4 byte invece di 1).
UTF-16 si è affermato come formato di visualizzazione in sistemi operativi come Apple macOS e Microsoft Windows e viene utilizzato anche in molti framework di sviluppo software. È una delle codifiche UTF più vecchie ancora in uso. La sua struttura è particolarmente adatta per la codifica di caratteri in lingua non latina, perché occupa poco spazio in memoria. La maggior parte dei caratteri può essere rappresentata con 2 byte (16 bit). Solo in caso di caratteri rari la lunghezza può raddoppiare fino a 4 byte.
Efficiente e scalabile: UTF-8
UTF-8 è costituito da un massimo di quattro sequenze di bit, ciascuna delle quali è composta da 8 bit. Il predecessore ASCII invece usa sequenze di bit composte da 7 bit. Entrambe le codifiche definiscono i primi 128 caratteri codificati in modo equivalente. Ciascuno dei caratteri provenienti principalmente dal mondo anglofono è dunque coperto da un byte. Per le lingue con alfabeto latino, questo formato garantisce un utilizzo più efficiente dello spazio di archiviazione. I sistemi operativi Unix e Linux lo utilizzano internamente. La codifica UTF-8 svolge il suo ruolo più importante tuttavia in relazione alle applicazioni Internet, in particolare nella rappresentazione di testi sul World Wide Web o nella posta elettronica.
Grazie alla struttura auto-sincronizzante, la leggibilità viene mantenuta nonostante la lunghezza variabile per carattere. Senza la restrizione Unicode UTF-8 potrebbe codificare un totale di 231 (= 4.398.046.511.104) caratteri. Quelli effettivi sono 221, per via del limite dei 4 byte imposto dallo standard Unicode, il che è più che sufficiente. Lo stesso standard Unicode ha ancora dei piani vuoti per molti altri sistemi di scrittura.
L'assegnazione esatta evita sovrapposizioni tra punti di codice, che in passato hanno ostacolato la comunicazione. Anche UTF-16 e UTF-32 garantiscono un'assegnazione esatta, ma UTF-8 utilizza lo spazio di archiviazione in modo particolarmente efficiente per il sistema di scrittura latino ed è progettato per consentire la coesistenza semplice e la copertura di diversi sistemi di scrittura, permettendone la visualizzazione simultanea e sensata all'interno di un campo di testo senza problemi di compatibilità.
Nozioni di base: codifica e composizione di UTF-8
La codifica UTF-8 offre diversi vantaggi come la retrocompatibilità con ASCII e la sua struttura auto-sincronizzante che facilita agli sviluppatori l'identificazione di fonti di errore anche in un secondo momento. UTF utilizza solo 1 byte per ciascuno dei caratteri ASCII. Il numero totale di sequenze di bit può essere riconosciuto dalle prime cifre del numero binario. Poiché il codice ASCII è composto solo da 7 bit, la prima cifra è 0. Lo 0 occupa lo spazio di archiviazione corrispondente a un byte completo e segnala l'inizio di una sequenza senza byte successivi. Se codificassimo il nome "UTF-8" come numero binario con la codifica UTF-8, questo apparirebbe come segue:
Carattere | U | T | F | - | 8 |
---|---|---|---|---|---|
UTF-8, binario | 01010101 | 01010100 | 01000110 | 00101101 | 00111000 |
Punto di codice Unicode, esadecimale | U+0055 | U+0054 | U+0046 | U+002D | U+0038 |
La codifica UTF-8 assegna i caratteri ASCII, come quelli usati nella tabella, a una singola sequenza di bit. Tutti i caratteri seguenti e simboli all'interno dello standard Unicode sono composti da due a quattro sequenze a 8 bit. La prima sequenza è lo start byte, o byte iniziale, le sequenze successive sono invece i byte successivi. Gli start byte con byte successivi iniziano sempre con 11. I byte successivi, invece, iniziano sempre con 10. Se cercate manualmente un determinato punto nel codice, potete dunque riconoscere l'inizio di un carattere codificato dai marcatori 0 e 11. Il primo carattere stampabile a più byte è il punto esclamativo invertito:
Carattere | ¡ |
---|---|
UTF-8, binario | 11000010 10100001 |
Punto di codice Unicode, esadecimale | U+00A1 |
La codifica del prefisso impedisce che all'interno di una sequenza di byte venga codificato un altro carattere. Se un flusso di byte inizia nel mezzo di un documento, il computer visualizza comunque correttamente i caratteri leggibili, poiché quelli incompleti non vengono rappresentati affatto. Se cercate l'inizio di un carattere, tenendo a mente il limite dei 4 byte, dovete tornare indietro in qualsiasi punto di un massimo di tre sequenze di byte per trovare quello iniziale.
Un altro elemento importante a livello strutturale è che la quantità di 1 all'inizio dello start byte indica la lunghezza della sequenza di byte. Come mostrato sopra, 110xxxxx sta per 2 byte. 1110xxxx sta per 3 byte e 11110xxx per 4 byte. In Unicode il valore di byte assegnato corrisponde al numero del carattere, consentendo un ordine lessicale. Tuttavia ci sono delle lacune. L'intervallo Unicode da U+007F a U+009F include numeri di controllo non assegnati. In questo intervallo lo standard UTF-8 non assegna caratteri stampabili, ma solo comandi.
La codifica UTF-8 può, come accennato sopra, unire teoricamente fino a otto sequenze di byte, ma Unicode prescrive una lunghezza massima di 4 byte. Di conseguenza, le sequenze di byte composte da 5 o più byte sono invalide di default. D'altra parte, questa limitazione riflette l'obiettivo di rappresentare il codice in modo più compatto, ovvero nella maniera più efficiente, in termini di spazio di archiviazione, e più strutturata possibile.
Una regola di base nell'utilizzo di UTF-8 prevede la predilezione della codifica più breve possibile. La lettera ä, ad esempio, viene codificata utilizzando 2 byte: 11000011 10100100. Teoricamente si potrebbero combinare i punti di codice della lettera a (01100001) e del carattere di dieresi ¨ (11001100 10001000) per rappresentare la ä: 01100001 11001100 10001000. Nel caso di UTF-8 questa forma è tuttavia considerata una codifica troppo estesa e quindi inammissibile.
È per via di questa regola che le sequenze di byte che iniziano con 192 e 193 non sono ammesse, perché possono rappresentare con 2 byte caratteri dell'intervallo ASCII (0-127) che sono già codificati con 1 byte.
Alcuni intervalli di valori Unicode non sono stati definiti per UTF-8 perché restino a disposizione dei surrogati UTF-16. La panoramica mostra quali byte in UTF-8, nello standard Unicode, sono validi secondo l'Internet Engineering Task Force (IETF) (gli intervalli contrassegnati in verde sono byte validi, quelli contrassegnati in rosso non sono validi).
Conversione dal sistema esadecimale Unicode a quello binario UTF-8
A differenza degli umani che usano un sistema decimale per comunicare, i computer utilizzano esclusivamente numeri binari per codificare le informazioni. Un punto di incontro tra queste due forme è il sistema esadecimale, che consente di rappresentare le lunghe sequenze di bit in modo più compatto. Questo sistema utilizza le cifre da 0 a 9 e le lettere da A ad F e rappresenta i numeri in base 16. Come quarta potenza di 2, il sistema esadecimale è più adatto del sistema decimale per raffigurare intervalli di byte a otto cifre.
Una cifra esadecimale equivale a una sequenza di quattro bit ("nibble") all'interno dell'ottetto. Questo significa che sono sufficienti appena due cifre esadecimali per rappresentare un byte che nel sistema binario richiederebbe ben otto cifre. Unicode utilizza il sistema esadecimale per descrivere la posizione di un carattere all'interno del proprio sistema, da cui è possibile calcolare il numero binario e quindi il punto di codice UTF-8.
Innanzitutto, il numero esadecimale deve essere convertito in numero binario. Successivamente, i punti di codice vanno adattati alla struttura della codifica UTF-8. La seguente panoramica evidenzia quanti punti di codice rientrano in una sequenza di byte e che tipo di struttura corrisponde a quale intervallo di valori Unicode. Potete utilizzarla per facilitare la strutturazione.
Dimensione in byte | Bit liberi | Primo punto di codice Unicode | Ultimo punto di codice Unicode | Start byte / byte 1 | Byte successivo 2 | Byte successivo 3 | Byte successivo 4 |
---|---|---|---|---|---|---|---|
1 | 7 | U+0000 | U+007F | 0xxxxxxx | |||
2 | 11 | U+0080 | U+07FF | 110xxxxx | 10xxxxxx | ||
3 | 16 | U+0800 | U+FFFF | 1110xxxx | 10xxxxxx | 10xxxxxx | |
4 | 21 | U+10000 | U+10FFFF | 11110xxx | 10xxxxxx | 10xxxxxx | 10xxxxxx |
Secondo l'ordine lessicale utilizzato per la numerazione dei punti di codice Unicode e dei numeri binari UTF-8, per ogni intervallo di codice è possibile presumere un dato numero di byte. All'interno dell'intervallo U+0800 e U+FFFF, la codifica UTF-8 utilizza quindi 3 byte. I bit disponibili per rappresentare il punto di codice di un simbolo sono 16. Un numero binario calcolato viene assegnato nello schema UTF-8 da destra a sinistra, riempiendo gli spazi vuoti a sinistra con zeri.
Esempio di calcolo:
Il carattere ᅢ (Hangul Junseong, Ä) corrisponde in Unicode a U+1162. Per calcolare il numero binario convertite innanzitutto il numero esadecimale in un numero decimale. Ogni cifra del numero corrisponde alla rispettiva potenza di 16. La cifra a destra ha il valore più basso con 160 = 1. Partendo da destra, moltiplicate il valore numerico della cifra per la potenza. Poi sommate i risultati.
2 | * | 1 | = | 2 |
---|---|---|---|---|
6 | * | 16 | = | 96 |
1 | * | 256 | = | 256 |
1 | * | 4096 | = | 4096 |
4450 | 4450 | 4450 | 4450 | 4450 |
Il numero decimale calcolato è 4450. Ora convertite questo numero in un numero binario. Dividete innanzitutto il numero per 2 con resto fino a quando il risultato è 0. Il resto, scritto da destra a sinistra, è il numero binario.
4450 | : 2 | = | 2225 | Resto: | 0 |
---|---|---|---|---|---|
2225 | : 2 | = | 1112 | Resto: | 1 |
1112 | : 2 | = | 556 | Resto: | 0 |
556 | : 2 | = | 278 | Resto: | 0 |
278 | : 2 | = | 139 | Resto: | 0 |
139 | : 2 | = | 69 | Resto: | 1 |
69 | : 2 | = | 34 | Resto: | 1 |
34 | : 2 | = | 17 | Resto: | 0 |
17 | : 2 | = | 8 | Resto: | 1 |
8 | : 2 | = | 4 | Resto: | 0 |
4 | : 2 | = | 2 | Resto: | 0 |
2 | : 2 | = | 1 | Resto: | 0 |
1 | : 2 | = | 0 | Resto: | 1 |
Risultato: 1000101100010 | Risultato: 1000101100010 | Risultato: 1000101100010 | Risultato: 1000101100010 | Risultato: 1000101100010 | Risultato: 1000101100010 |
Esadecimale | Binario |
---|---|
0 | 0000 |
1 | 0001 |
2 | 0010 |
3 | 0011 |
4 | 0100 |
5 | 0101 |
6 | 0110 |
7 | 0111 |
8 | 1000 |
9 | 1001 |
A | 1010 |
B | 1011 |
C | 1100 |
D | 1101 |
E | 1110 |
F | 1111 |
Grazie a questo esempio dettagliato potete seguire facilmente i diversi passaggi del calcolo. Per convertire rapidamente e facilmente i numeri esadecimali in numeri binari è sufficiente sostituire ogni cifra esagonale con le corrispondenti quattro cifre binarie.
Per il punto di codice Unicode U+1162 unite le cifre come segue:
x1 = b0001
x1 = b0001
x6 = b0110
x2 = b0010
x1162 = b0001000101100010
Il codice UTF-8 prevede 3 byte per il punto di codice U+1162, perché il punto di codice si trova nell'intervallo tra U+0800 e U+FFFF. Lo start byte inizia quindi con 1110. I due byte successivi iniziano ciascuno con 10. Nei bit liberi, che non specificano la struttura, aggiungete il numero binario da destra a sinistra. Riempite le restanti posizioni di bit nel byte iniziale con 0 fino a quando l'ottetto non è completo. Si ottiene dunque la seguente codifica UTF-8:
11100001 10000101 10100010 (il punto di codice inserito è in grassetto)
Carattere | Punto di codice Unicode, esadecimale | Decimale | Numero binario | UTF-8 |
---|---|---|---|---|
ᅢ | U+1162 | 4450 | 1000101100010 | 11100001 10000101 10100010 |
UTF-8 nell'editor
Sebbene UTF-8 sia lo standard più diffuso su Internet, i semplici editor di testo non salvano necessariamente di default i testi in questo formato. Microsoft Notepad utilizza, per esempio, di default una codifica chiamata "ANSI" (che cela la codifica "Windows-1252" basata su ASCII). Se desiderate convertire un file di testo da Microsoft Word in UTF-8, ad esempio per visualizzare diversi sistemi di scrittura, procedete come segue: andate su "Salva con nome" e selezionate l'opzione "Testo normale" nel campo Salva come.
Si apre la finestra di pop-up "Conversione file". In Codifica testo, selezionate "Altra codifica" e poi dall'elenco "Unicode (UTF-8)". Nel menu a discesa "Termina righe con" selezionate "Ritorno a capo / Nuova riga" o "CR/LF". Con questi semplici passaggi potete convertire un file in Unicode con UTF-8.
Se aprite un file di testo non contrassegnato del quale non sapete quale codifica sia stata usata si possono verificare problemi durante la modifica. In questi casi in Unicode viene utilizzato il Byte Order Mark (BOM), un carattere invisibile che indica se il documento è in formato Big-Endian o Little-Endian. Se un programma decodifica un file in UTF-16 Little-Endian usando UTF-16 Big-Endian, il testo viene visualizzato in modo errato. I documenti basati sul set di caratteri UTF-8 non presentano questo problema, poiché l'ordine dei byte viene sempre letto come una sequenza di byte Big-Endian. In questo caso il BOM serve unicamente come indicazione della codifica in UTF-8.
In alcune codifiche (UTF-16 e UTF-32) i caratteri rappresentati da più di un byte possono avere il byte più significativo nella prima posizione anteriore (sinistra) o nell'ultima posizione posteriore (destra). Se il byte più significativo ("Most Significant Byte", MSB) si trova nella parte anteriore, la codifica viene definita "Big-Endian", se l'MSB si trova invece nella parte posteriore, viene definita "Little-Endian".
Il BOM viene posizionato prima di un flusso di dati o all'inizio di un file. Questo marcatore ha la priorità su tutte le altre istruzioni, compreso l’header HTTP. Il BOM funge come una sorta di indicatore delle codifiche Unicode e corrisponde al punto di codice U+FEFF. La rappresentazione in codice del BOM cambia a seconda della codifica utilizzata.
Formato di codifica | BOM, punto di codice: U+FEFF (hex) |
---|---|
UTF-8 | EF BB BF |
UTF-16 Big-Endian | FE FF |
UTF-16 Little-Endian | FF FE |
UTF-32 Big-Endian | 00 00 FE FF |
UTF-32 Little-Endian | FF FE 00 00 |
Il Byte Order Mark non viene utilizzato se è esplicitamente vietato dal protocollo o se i vostri dati sono già assegnati a una determinata tipologia. In base al protocollo alcuni programmi prevedono caratteri ASCII. Dato che la codifica UTF-8 è retrocompatibile con quella ASCII e il suo ordine di byte è fisso, non è necessario utilizzare un BOM. Unicode raccomanda infatti di non utilizzare il BOM con UTF-8. Tuttavia, poiché i codici più vecchi possono presentare dei BOM che possono causare problemi, è importante identificarli come tali.
In sintesi: la codifica UTF-8 migliora la comunicazione internazionale
La codifica UTF-8 offre molti vantaggi, oltre alla sua compatibilità con quella ASCII. Grazie alla lunghezza variabile delle sequenze di byte e all'enorme numero di punti di codice possibili, è in grado di visualizzare un numero estremamente elevato di sistemi di scrittura diversi. Lo spazio di archiviazione viene utilizzato in modo efficiente.
L'introduzione di standard uniformi come Unicode facilita la comunicazione internazionale. Grazie all'uso di sequenze di byte auto-sincronizzanti, la codifica UTF-8 è caratterizzata da un basso tasso di errore nella rappresentazione dei caratteri. Anche se si verifica un errore di codifica o di trasmissione, è possibile trovare facilmente l'inizio di ogni carattere identificando nella sequenza il byte che inizia con 0 o 11. UTF-8 è diventato lo standard di codifica indiscusso per i siti web con un tasso di utilizzo superiore al 90%.