Caratteri Unicode invisibili: cosa sono e cosa compromettono

Un riferimento pratico ai caratteri presenti nel tuo testo ma non sullo schermo.

I caratteri Unicode invisibili non occupano spazio visibile ma fanno comunque parte della stringa. I più comuni sono gli spazi a larghezza zero, i trattini morbidi, i byte-order mark, gli spazi non separabili e i segni direzionali. Ognuno ha uno scopo tipografico legittimo, e ognuno compromette ricerca, convalida, parsing e deduplicazione quando sopravvive a un copia-incolla.

I caratteri che incontrerai davvero

Carattere Code point Scopo Cosa compromette
Spazio a larghezza zero U+200B Suggerisce un punto di interruzione di riga Ricerca, confronto tra stringhe
Congiungitore zero-width negativo U+200C Impedisce l’unione delle lettere Legature, testo arabo e indiano
Congiungitore zero-width U+200D Forza l’unione delle lettere Sequenze emoji, lunghezza del testo
Trattino morbido U+00AD Trattino visibile solo a fine riga Ricerca, testo copiato
Spazio non separabile U+00A0 Spazio che non va mai a capo Trim, parsing CSV, codice
Spazio sottile non separabile U+202F Spazio sottile prima della punteggiatura Come sopra
Byte-order mark U+FEFF Segna la codifica all’inizio del file Prima colonna CSV, parsing JSON
Word joiner U+2060 Impedisce l’interruzione, senza larghezza Ricerca
Segno sinistra-destra U+200E Imposta la direzione del testo Ordine di visualizzazione, trim
Segno destra-sinistra U+200F Imposta la direzione del testo Ordine di visualizzazione
Override RTL U+202E Forza la visualizzazione da destra a sinistra Falsificazione dei nomi file
Separatore di riga U+2028 Interruzione di riga Unicode Stringhe letterali JavaScript
Separatore di paragrafo U+2029 Interruzione di paragrafo Unicode JSON nei parser più vecchi

Ognuno di questi caratteri ha un compito reale. Il problema non è mai la loro esistenza: è che viaggiano in silenzio tra sistemi che li trattano in modo diverso.

Da dove arrivano

  • Copia da pagine web. I siti inseriscono spazi a larghezza zero per controllare dove vanno a capo le stringhe lunghe, e trattini morbidi per il testo giustificato. Entrambi vengono copiati insieme al testo.
  • Word processor. Gli spazi non separabili vengono inseriti automaticamente per tenere un numero con la sua unità di misura, o un nome su una sola riga.
  • Estrazione da PDF. Copiando da un PDF compaiono spesso trattini morbidi nei punti in cui il tipografo ha spezzato le parole tra le righe, oltre a spazi non separabili dovuti alla giustificazione.
  • Interfacce di chat IA. L’output viene renderizzato come HTML, quindi la copia porta con sé i caratteri di spaziatura usati dalla formattazione, di solito U+00A0 e U+202F.
  • Conversioni di codifica. Un byte-order mark compare quando un file viene salvato come UTF-8 con BOM, cosa comune su Windows.
  • Inserimento deliberato. I caratteri a larghezza zero sono stati usati per marcare i documenti e rintracciare le fughe di notizie, e per far passare testo oltre i filtri per parole chiave.

Lo schema è sempre lo stesso: entrano quando il testo passa da un contesto di presentazione a un contesto di dati.

Come creano problemi

La ricerca fallisce in silenzio. Uno spazio a larghezza zero dentro una parola significa che cercarla non restituisce nulla. Nulla indica il perché: la parola è visibilmente lì.

La convalida rifiuta input corretti. Un indirizzo email con uno spazio non separabile finale non supera il controllo di formato. L’utente vede un indirizzo corretto e un messaggio di errore senza senso.

I confronti falliscono. "Ada" === "Ada\u200B" è falso. La deduplicazione ignora duplicati evidenti; le ricerche non restituiscono nulla; i join perdono righe.

Il trim non aiuta. La maggior parte delle implementazioni di trim() rimuove solo gli spazi ASCII. Uno spazio non separabile sopravvive a .trim() in molti linguaggi: ecco perché «l’ho già ripulito con trim» è un vicolo cieco così frequente.

Il codice non compila. Uno spazio non separabile al posto di uno spazio normale produce un errore di sintassi su una riga che sembra perfetta.

Le importazioni CSV si corrompono. Un byte-order mark si attacca alla prima intestazione, quindi id diventa \uFEFFid e la prima colonna non viene mappata, senza alcun avviso.

La lunghezza è sbagliata. I congiungitori zero-width nelle sequenze emoji fanno sì che una singola emoji visibile sia composta da più code point, quindi i limiti di caratteri rifiutano input che sembrano ampiamente nei limiti.

I nomi dei file possono ingannare. U+202E inverte il testo visualizzato, quindi report\u202Egnp.exe può apparire come reportexe.png. È un vero problema di sicurezza, non una curiosità.

Come trovarli e rimuoverli

Prima analizza. Incolla il testo nello scanner di caratteri invisibili. Indica quali tipi sono presenti e in che quantità, poi li rimuove su richiesta. Vedere prima i conteggi ti dice se hai a che fare con un singolo carattere perso o con una contaminazione sistematica.

In un editor. VS Code evidenzia di default la maggior parte dei caratteri invisibili tramite l’impostazione unicode-highlight. Per un trova-e-sostituisci con regex, questo pattern cattura l’insieme più comune:

[\u200B-\u200F\u00AD\uFEFF\u2060\u202A-\u202E\u2028\u2029]

Nel codice, normalizza al confine: quando i dati arrivano, non quando vengono usati:

const clean = input
  .replace(/[\u200B-\u200F\u2060\uFEFF\u00AD\u202A-\u202E]/g, "")
  .replace(/[\u00A0\u202F]/g, " ")
  .normalize("NFC")
  .trim();

Nota il trattamento in due passaggi: i caratteri a larghezza zero vengono eliminati, mentre gli spazi non separabili vengono convertiti in spazi normali: eliminarli unirebbe le parole.

Non rimuovere alla cieca. I congiungitori zero-width sono strutturali nelle sequenze emoji e nelle scritture araba, persiana e in molte scritture indiane, dove rimuoverli cambia il modo in cui le parole vengono renderizzate. Rimuovi in modo aggressivo in identificatori, chiavi e codice; con cautela nei contenuti rivolti agli utenti.

Per la questione specifica se gli strumenti IA inseriscano questi caratteri di proposito, vedi ChatGPT nasconde caratteri invisibili?. L’equivalente visibile ma altrettanto problematico è trattato in perché le virgolette curve rompono il codice.

Domande frequenti

Cos’è uno spazio a larghezza zero?

È U+200B, un carattere che non occupa larghezza visibile ma fa comunque parte della stringa. I siti web lo usano per suggerire dove una parola lunga può andare a capo. Compromette ricerca e confronto tra stringhe perché il testo sembra identico mentre i byte sottostanti sono diversi.

Perché la ricerca fallisce su un testo che è chiaramente presente?

Quasi sicuramente c’è un carattere invisibile dentro la parola, di solito uno spazio a larghezza zero o un trattino morbido raccolto da una pagina web o da un PDF. Il testo visualizzato corrisponde a ciò che hai digitato, ma la stringa sottostante no.

trim() rimuove gli spazi non separabili?

Di solito no. La maggior parte delle implementazioni di trim rimuove solo gli spazi ASCII, quindi uno spazio non separabile (U+00A0) sopravvive. Ecco perché un input che sembra non avere spazi iniziali o finali fallisce comunque la convalida dopo il trim.

Come rimuovo i caratteri invisibili dal testo?

Incollalo in uno scanner che li elenca e li rimuove, oppure usa una regex che copra U+200B–U+200F, U+00AD, U+FEFF, U+2060 e U+202A–U+202E. Elimina i caratteri a larghezza zero, ma converti gli spazi non separabili in spazi normali invece di eliminarli, altrimenti le parole si uniranno.

I caratteri invisibili sono mai utili?

Sì. I trattini morbidi controllano le cesure nel testo giustificato, gli spazi non separabili tengono i valori uniti alle unità di misura, e i congiungitori zero-width sono essenziali per le sequenze emoji e per le scritture araba, persiana e indiane. Rimuovili in modo aggressivo nel codice e negli identificatori, con cautela nella prosa.

Strumenti citati in questa guida

Guide correlate