Ispettore di caratteri Unicode
Scompone il testo nei suoi punti di codice con nomi, categorie, blocchi e codifiche, e segnala caratteri invisibili, bidi e sosia.
Che cosa fa questo strumento
Incolla un testo qualsiasi e torna scomposto nei singoli caratteri di cui è davvero fatto. Ognuno riceve il suo nome Unicode, il suo numero U+, che tipo di carattere è, a quale sistema di scrittura e a quale blocco appartiene, i byte che occupa in UTF-8 e UTF-16 e la sequenza di escape che richiede in sei formati diversi.
Quasi tutti arrivano qui perché qualcosa non torna. Una stringa è più lunga di quanto sembri, due valori che appaiono identici si rifiutano di risultare uguali, un nome utente è stato respinto per caratteri che sembrano lettere normali, oppure una riga di codice si legge diversamente da come si comporta. Tutti e quattro hanno la stessa forma: un carattere che fa qualcosa che lo schermo non mostra.
Caratteri, punti di codice e grafemi
La parola «carattere» indica almeno tre cose diverse, e confonderle è l’origine di quasi tutta la confusione su Unicode. Un punto di codice è un valore dello standard Unicode, scritto U+0041 o U+1F600. Un grafema è ciò che chi legge conta come un carattere. Spesso non sono lo stesso numero.
- Una «a» semplice è 1 grafema, 1 punto di codice e 1 unità UTF-16: tutto coincide.
- U+00E9, la é precomposta, è 1 grafema, 1 punto di codice e 1 unità UTF-16.
- U+0065 U+0301, la stessa lettera scritta come e più un accento combinante, è 1 grafema ma 2 punti di codice e 2 unità UTF-16.
- U+1F600, la faccina sorridente, è 1 grafema e 1 punto di codice ma 2 unità UTF-16: vive sopra il BMP, quindi JavaScript la conserva come coppia surrogata.
- L’emoji della famiglia è 1 grafema, 5 punti di codice e 8 unità UTF-16: tre persone unite da due congiuntori invisibili.
L’emoji della famiglia è il caso che coglie tutti in fallo. Sembra una cosa sola, JavaScript riporta lunghezza 8 e una colonna di database da 5 caratteri non lo contiene. Questo strumento raggruppa i punti di codice sotto il grafema che costruiscono, così entrambe le letture sono visibili insieme invece di dover essere riconciliate a mente.
I caratteri che non si vedono
Un numero sorprendente di caratteri Unicode non disegna proprio nulla. Esistono per governare il comportamento del testo che li circonda e, non lasciando traccia, sopravvivono a copia, incolla e revisione senza che nessuno se ne accorga. Sono questi quelli che lo strumento indica invece di limitarsi a elencarli:
- Spazio, congiuntore e disgiuntore a larghezza zero (U+200B, U+200D, U+200C): nessuna larghezza, nessun segno, e rompono ogni confronto fra stringhe che toccano.
- Trattino morbido (U+00AD): un suggerimento su dove una parola può spezzarsi, invisibile finché non accade.
- Spazio unificatore (U+00A0): sembra identico a uno spazio ed è un altro carattere, per questo resiste così a lungo nei file di configurazione.
- Indicatore dell’ordine dei byte (U+FEFF): spesso resta all’inizio di un file, dove diventa un primo carattere invisibile del primo valore.
- Controlli e forzature bidi (da U+202A a U+202E, da U+2066 a U+2069): riordinano il testo attorno a sé.
I controlli bidi meritano una menzione a parte. Sono stati aggiunti perché ebraico e arabo potessero mescolarsi correttamente al testo latino, e questo lavoro lo svolgono bene. Ma una forzatura collocata dentro un commento o una stringa può far apparire una riga di codice in un ordine del tutto diverso da quello che legge il compilatore, il che significa che un revisore può approvare codice che fa tutt’altro. Questa classe di trucchi è nota come Trojan Source, ed è il motivo per cui questo strumento segnala ogni controllo bidi separatamente dagli altri caratteri invisibili.
Caratteri sosia e scritture mescolate
Unicode contiene molti caratteri visivamente identici alle lettere ASCII e che non lo sono. La а cirillica, la ο greca e la a latina occupano punti di codice diversi e si disegnano allo stesso modo nella maggior parte dei font. Un dominio o un nome utente costruito con esse sembra corretto e punta altrove.
Segnalare ogni sosia non ASCII sarebbe inutile: tingerebbe di rosso ogni parola russa, greca o ebraica, e un avviso che scatta sul testo normale è un avviso che si impara a saltare. Perciò la regola qui è quella dello standard di sicurezza Unicode: una parola è sospetta quando le lettere al suo interno provengono da più di un sistema di scrittura:
- «paypal.com» scritto interamente in lettere latine: non c’è nulla da dire.
- La stessa parola con una er e una a cirilliche davanti al «ypal» latino: una parola, due sistemi di scrittura, segnalata.
- Un’intera parola russa in cirillico: un solo sistema di scrittura, testo ordinario, non segnalata.
- Una frase giapponese che usa han, hiragana e katakana insieme: tre scritture, un solo sistema di scrittura, non segnalata.
Quest’ultimo caso è il motivo per cui la regola richiede attenzione. Il giapponese si scrive con tre scritture insieme e il coreano ne mescola due, quindi un test ingenuo del tipo «più di una scrittura è sospetto» dichiarerebbe attacco ogni frase giapponese. Le combinazioni che sono davvero un solo sistema di scrittura vengono trattate come una, e questo mantiene l’avviso significativo.
Normalizzazione, ovvero perché due stringhe identiche differiscono
Alcuni caratteri si possono scrivere in più di un modo. La lettera é è o il singolo punto di codice U+00E9 o la coppia U+0065 U+0301: una e semplice seguita da un accento acuto combinante. Entrambe appaiono identiche. Nessuna delle due è sbagliata. Uguali non sono.
La normalizzazione è il procedimento con cui si sceglie una grafia, e ne esistono quattro forme. NFC compone dove può ed è quanto il web presume per impostazione predefinita; NFD invece scompone. Le due forme K vanno oltre e ripiegano anche i caratteri di compatibilità — la A a larghezza intera diventa una A normale e la legatura fi diventa fi — il che è utile per ricerca e confronto e con perdita per l’archiviazione.
Il pannello qui mostra tutte e quattro le forme di ciò che incolli e segna quali differiscono dall’input. Se nessuna differisce, il testo è già normalizzato e la discrepanza che stai inseguendo è altrove. Se alcune differiscono, l’hai trovata.
Nomi, categorie, scritture e blocchi
Ogni carattere assegnato porta con sé quattro elementi di identità, e rispondono a domande diverse. Il nome è il carattere stesso. La categoria generale dice che genere di cosa sia: una lettera minuscola, un segno combinante, un simbolo di valuta, un controllo di formato. La scrittura è il sistema di scrittura a cui appartiene. Il blocco è l’intervallo di punti di codice in cui è stato assegnato, che è un fatto organizzativo e non linguistico.
Blocco e scrittura si confondono facilmente e spesso non coincidono. Le lettere latine vivono in almeno una mezza dozzina di blocchi, e un blocco può contenere caratteri di più scritture. Quando vuoi sapere «di quale alfabeto si tratta» la risposta è la scrittura; quando vuoi sapere «dove si colloca nello standard» è il blocco.
Nomi, scritture e blocchi sono mostrati qui in inglese in ogni lingua, deliberatamente. Sono identificatori definiti dallo standard, non prosa: U+200D si chiama ZERO WIDTH JOINER in tutto il mondo, e tradurlo lo renderebbe impossibile da cercare.
Codifiche ed escape
Un punto di codice è un numero; diventa byte solo quando si sceglie una codifica. UTF-8 usa da uno a quattro byte e lascia ASCII invariato, ed è per questo che ha vinto. UTF-16 usa una o due unità da 16 bit ed è ciò di cui sono fatte le stringhe JavaScript, motivo per cui .length riporta 2 per una singola emoji.
U+0041 41 # UTF-8: un byte, come in ASCII U+00E9 C3 A9 # UTF-8: due byte U+20AC E2 82 AC # UTF-8: tre byte U+1F600 F0 9F 98 80 # UTF-8: quattro byte
Ogni riga qui dà anche il carattere scritto come escape per JavaScript, JSON, HTML, CSS, URL e Python, pronto da copiare. Non sono tutti uguali, e sopra il BMP la differenza conta: JavaScript e Python hanno un escape per punto di codice e lo usano, mentre JSON non ne ha alcuno e deve compitare un’emoji come le sue due metà surrogate.
Domande frequenti
- Perché la mia stringa è più lunga del numero di caratteri che vedo?
- O contiene caratteri invisibili, o contiene grafemi costruiti con più punti di codice. Entrambe le cose sono mostrate qui: i caratteri invisibili vengono segnalati e i grafemi da più punti di codice vengono raggruppati, così vedi il carattere singolo e i diversi valori di cui è composto.
- Due stringhe sembrano identiche e non sono uguali. Perché?
- Di solito la normalizzazione. Una lettera accentata può essere un punto di codice oppure una lettera più un segno combinante, ed entrambe si disegnano allo stesso modo. Incolla qui ciascuna stringa e confronta gli elenchi di punti di codice, oppure guarda quali forme di normalizzazione differiscono dall’input.
- Che cos’è uno spazio a larghezza zero e come è finito nel mio testo?
- È un carattere senza larghezza e senza segno, usato per suggerire un’occasione di andare a capo. Di solito arriva copiando testo da una pagina web, da un editor di documenti o da un client di chat e, poiché non appare nulla, sopravvive a ogni revisione finché qualcosa confronta stringhe e fallisce.
- Qual è la differenza fra un punto di codice e un carattere?
- Un punto di codice è un valore Unicode. Un carattere nel senso comune è un grafema — ciò che chi legge conta come uno — e un grafema può valere più punti di codice. Un’emoji di famiglia sono cinque punti di codice e un grafema.
- Perché lo strumento dice che alcuni caratteri non hanno nome?
- Per quattro motivi diversi, e dice quale. I surrogati e i punti di codice a uso privato non hanno nome nello standard, quelli non assegnati non hanno nulla da nominare, e un carattere di un blocco storico consultato di rado ha un nome che questa build non porta con sé: la tabella dei nomi conserva i blocchi da cui la gente incolla davvero invece di spedirli tutti.
- Un avviso di scritture mescolate è la prova di un attacco?
- No. Significa che una singola parola trae le sue lettere da più di un sistema di scrittura, che è la forma di un nome contraffatto ed è anche qualcosa che il testo ordinario fa di tanto in tanto. È un motivo per guardare, non un verdetto.
- Qualcosa di ciò che incollo viene inviato a un server?
- No. Il database dei caratteri viene scaricato con la pagina e tutto gira nel tuo browser; nulla di ciò che digiti viene caricato o registrato, e funziona senza connessione di rete.