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.

Input

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.

Caratteri nascosti, e il testo uscito da un modello di IA

Buona parte del testo che si incolla qui non è stato digitato da chi lo incolla: viene da un documento, da una pagina web, da un ticket o dalla risposta di un assistente, e la domanda con cui si arriva è se si porti dietro caratteri che non si vedono. È esattamente ciò a cui questo strumento risponde, e conviene dire fin dove arriva la risposta: il testo viene scomposto nei suoi punti di codice, ciascuno con il suo nome quando lo standard gliene dà uno, e quelli che meritano una seconda occhiata vengono contati in una riga sopra l’elenco. Se quella riga non compare, non è stato trovato nulla.

Ciò che un carattere nascosto dimostra è più stretto di quanto lasci intendere quasi tutto ciò che si scrive sull’argomento. Nulla dentro un punto di codice registra da dove sia arrivato: quello entrato copiando da un gestore di contenuti e quello entrato con una risposta generata sono lo stesso valore. Caratteri invisibili sono davvero stati segnalati nell’output di grandi modelli linguistici e, nel caso che ha attirato più attenzione — uno spazio unificatore stretto dove ci voleva uno spazio normale — il produttore del modello ha risposto che era un sottoprodotto dell’addestramento e non qualcosa messo lì di proposito, e le segnalazioni sono cessate in pochi giorni. Una scoperta qui dice quindi che cosa cancellare. Non dice che cosa abbia scritto la frase.

  • Spazi che non sono lo spazio normale. Lo spazio unificatore (U+00A0) e lo spazio unificatore stretto (U+202F) sono i due nominati più spesso; ciascuno è più o meno largo quanto uno spazio, ciascuno è un altro carattere, e questo strumento li segnala entrambi.
  • Caratteri senza alcuna larghezza. Lo spazio a larghezza zero (U+200B), il congiuntore di parole (U+2060) e l’indicatore dell’ordine dei byte (U+FEFF) non disegnano nulla, sopravvivono alla revisione e rompono il primo confronto che toccano. Vengono segnalati come invisibili.
  • Il blocco Tags (da U+E0000 a U+E007F). Contiene un punto di codice invisibile per ogni carattere ASCII stampabile — U+E0041 si chiama TAG LATIN CAPITAL LETTER A — così che un’intera frase possa essere compitata dentro un testo che non mostra proprio nulla. Ciascuno è nominato qui e segnalato come invisibile.
  • La punteggiatura a cui si dà la colpa e che non è nascosta. La lineetta lunga (U+2014) e l’apostrofo tipografico (U+2019) disegnano un segno visibile, quindi qui nulla li segnala; sono tipografia ordinaria, qualunque cosa li abbia prodotti.

Vale la pena tenere distinti due motivi per fare il controllo. Quello quotidiano è che i caratteri invisibili rompono le cose in silenzio: un confronto che dovrebbe riuscire, una chiave di ricerca, un campo che rifiuta ciò che scrivi senza dire perché. L’altro è che il testo è letto da programmi oltre che da persone: i caratteri del blocco Tags erano pensati per le etichette di lingua, non disegnano nulla nella maggior parte degli editor e dei diff, e ciò che legge il testo invece di guardarlo li legge lo stesso. Se incolli per intero una risposta lunga, tieni presente che l’elenco delle righe viene troncato e il conteggio no: è preso su tutto ciò che hai incollato.

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.
Il testo di un modello di IA contiene caratteri invisibili?
A volte, e mai in un modo che identifichi qualcosa. Il caso che ha attirato più attenzione è stato uno spazio unificatore stretto (U+202F) che compariva nelle risposte lunghe dove ci voleva uno spazio normale; il produttore del modello ha risposto che era un sottoprodotto dell’addestramento e non qualcosa messo lì di proposito, e in pochi giorni non lo si è più visto. Incolla il tuo testo qui e vedrai che cosa contiene. Ciò che non vedrai è da dove viene, perché un punto di codice non lo registra.
Come trovo ed elimino i caratteri nascosti nel mio testo?
Incollalo qui per trovarli: ogni punto di codice ha una riga, con il suo nome se ne ha uno, e quelli che meritano una seconda occhiata sono contati sopra l’elenco. Eliminarli è compito del tuo editor — qui nulla riscrive il tuo testo — ma una volta che sai quale punto di codice stai inseguendo, ogni riga te lo dà come escape in sei formati, che è ciò che vuole una casella di ricerca capace di leggerli.
Una lineetta lunga o un apostrofo tipografico è un carattere nascosto?
No. La lineetta lunga (U+2014) e l’apostrofo tipografico (U+2019) disegnano entrambi un segno sullo schermo, quindi qui nulla li segnala. Vengono spesso additati come indizio di scrittura generata, che è una questione di stile a cui un ispettore di caratteri non può rispondere. Restano comunque utili da conoscere per un altro motivo: nessuno dei due è ASCII, quindi entrambi possono disturbare qualcosa che se lo aspetta, e una stringa scritta con essi non risulta uguale alla stessa stringa digitata con un trattino e un apostrofo dritto.

Strumenti correlati