Le emoji sono più difficili di quanto sembri: perché «basta toglierle» non ha una risposta in una riga
Pubblicato il 29/09/2025 · 13 min di lettura · Strumenti testo e lingua
Daniel Okonkwo — Sviluppatore front-end e redattore Tech presso Allin
Performance web · Formati di file
Verificato su 5 fonti
Non esiste un'espressione regolare semplice per togliere le emoji, perché non esiste una definizione semplice di che cosa sia una emoji. Una sola immagine sullo schermo può essere un punto di codice (😀 è U+1F600), un carattere base più un selettore di variante invisibile (❤️ è U+2764 U+FE0F), una base più un modificatore di tono della pelle (👍🏽 è U+1F44D U+1F3FD), una catena di caratteri uniti da giuntori a larghezza zero (👨👩👧👦 sono sette punti di codice e undici unità UTF-16), una coppia di indicatori regionali (🇵🇹 è U+1F1F5 U+1F1F9), o una sequenza di etichette di sette punti di codice. Nemmeno le proprietà a cui si ricorre coincidono: \p{Emoji} riconosce le cifre ASCII da 0 a 9 e anche # e *, così usarla per ripulire una frase cancella i numeri — l'abbiamo eseguita e «2026 revenue up 40%» è tornata come « revenue up %». \p{Extended_Pictographic} è la proprietà giusta per le immagini ma non copre toni della pelle né bandiere. La regola che funziona è segmentare il testo in gruppi di grafemi e togliere gruppi interi. La stessa regola aggiusta il troncamento, dove tagliare a un indice di unità di codice può spezzare una famiglia di quattro in due persone estranee o lasciare mezzo carattere che diventa U+FFFD.
Una emoji visibile può valere un punto di codice o quattordici unità UTF-16. Abbiamo lanciato tre espressioni regolari diffuse su una frase vera e ognuna si è rotta in modo diverso; una ha cancellato le cifre. Ecco perché, quale proprietà Unicode risponde a quale domanda, e la regola a gruppi di grafemi che funziona davvero.
Un'immagine, sei costruzioni diverse
Chiedi a un programma quanto è lungo un emoji e ottieni tre risposte a seconda dell'unità. Abbiamo misurato le stesse otto emoji in tre modi: in unità di codice UTF-16, cioè quello che .length restituisce in JavaScript; in punti di codice; e in gruppi di grafemi, cioè quello che un lettore chiamerebbe un carattere. Il conteggio in grafemi è 1 per ognuna. Gli altri due vanno da 1 a 14.
Il caso semplice è un singolo punto di codice: 😀 è U+1F600, due unità UTF-16 perché sta fuori dal piano multilingue di base. Poi si complica. ❤️ è U+2764 seguito da U+FE0F, un selettore di variante invisibile il cui unico compito è dire «disegna il carattere precedente come immagine, non come simbolo». 1️⃣ sono tre punti di codice: la cifra ASCII 1, lo stesso selettore e U+20E3 COMBINING ENCLOSING KEYCAP. 👍🏽 è un pollice in su seguito da U+1F3FD, un modificatore di tono che è a sua volta un carattere valido e compare come un quadrato colorato quando resta solo.
I casi difficili sono le sequenze. 👨👩👧👦 sono un uomo, una donna, una bambina e un bambino separati da tre copie di U+200D ZERO WIDTH JOINER: sette punti di codice, undici unità UTF-16, un'immagine. 👩💻 è una donna più un portatile uniti allo stesso modo, e 🏴☠️ è una bandiera nera unita a un teschio con un selettore di variante in fondo. Le bandiere funzionano in un modo ancora diverso: 🇵🇹 non è affatto un carattere di bandiera ma la coppia di indicatori regionali U+1F1F5 U+1F1F9, cioè le lettere P e T in un alfabeto speciale, e 🏴 è una bandiera nera seguita da cinque caratteri di etichetta invisibili che compitano un codice di suddivisione, più un terminatore: sette punti di codice, quattordici unità UTF-16.
La proprietà che sembra giusta e cancella i tuoi numeri
JavaScript, e ogni motore di espressioni regolari che supporta gli escape di proprietà Unicode, offre \p{Emoji}. È la scelta ovvia ed è quella sbagliata. Emoji è una proprietà di carattere che significa «questo carattere può partecipare a una emoji», e le cifre da 0 a 9 partecipano: sono le basi delle sequenze keycap. Anche # e *, per lo stesso motivo.
La conseguenza è facile da mostrare. Prendi la frase «2026 revenue up 40%», che non contiene alcuna emoji, ed esegui replace(/\p{Emoji}/gu, ""). Restituisce « revenue up %». Tutte le cifre sono sparite; il segno di percentuale, che non è un componente di emoji, sopravvive. La stessa espressione trasforma «Order #7 shipped ✅» in «Order shipped ». Esegui invece \p{Extended_Pictographic} sulla prima frase e torna intatta, che è il comportamento voluto.
Ma nemmeno Extended_Pictographic è una risposta completa, perché significa «questo carattere è un pittogramma» e diverse parti di una emoji non lo sono. Abbiamo provato ogni parte: il modificatore di tono U+1F3FD non è Extended_Pictographic, e nemmeno gli indicatori regionali che formano una bandiera. Esegui Extended_Pictographic da sola su una frase con 🇵🇹 e 👍🏽 e la bandiera sopravvive intera mentre il pollice sparisce e lascia dietro il suo tono, come un quadrato colorato solitario.
Emoji_Presentation è la proprietà che risponde a «questo comparirà come immagine?»
Alcuni caratteri appaiono per impostazione predefinita come immagine a colori e altri come testo monocromatico. Quella distinzione è esattamente ciò che Emoji_Presentation registra. Abbiamo sondato un insieme di caratteri e il taglio è netto: 😀, ⌚, 👍 e 🀄 hanno Emoji_Presentation, quindi si disegnano come immagine senza altro. ❤, ☺, ▶, ✔, ©, ® e ™ no; da soli si disegnano come glifi di testo nel font circostante.
È per questo che esiste il selettore di variante. U+FE0F è la richiesta di disegnare il carattere precedente come emoji, ed è ciò che trasforma ❤ in ❤️ e ▶ in ▶️. È anche invisibile, senza peso nella rappresentazione del lettore, e un carattere vero nella stringa: da qui il U+FE0F orfano che lascia una regex che toglie il pittogramma ma non il suo selettore. L'abbiamo misurato: «done ❤️» privata degli Extended_Pictographic torna come sei punti di codice: d, o, n, e, spazio, U+FE0F. Il risultato sembra pulito e non lo è.
Tre regex ingenue, tre fallimenti diversi
Abbiamo preso una frase — «Shipping to 🇵🇹 today 👨👩👧👦 — 40% off, thanks 👍🏽 #7 ❤️», cioè 63 unità UTF-16, 55 punti di codice e 46 gruppi di grafemi — e lanciato i tre approcci abituali, più un quarto costruito sui gruppi di grafemi.
L'approccio per intervallo di punti di codice, /[\u{1F300}-\u{1FAFF}]/gu, è quello che si trova in metà delle risposte online. Ha tolto i membri della famiglia e il pollice, ma la bandiera è sopravvissuta perché gli indicatori regionali stanno a U+1F1E6–U+1F1FF, sotto l'intervallo; il cuore è sopravvissuto perché U+2764 è molto più in basso; e i tre giuntori a larghezza zero che tenevano insieme la famiglia sono rimasti nella stringa come spazzatura invisibile.
L'approccio \p{Emoji} ha tolto la bandiera, la famiglia e il pollice, e anche il 40 e il 7 di #7, lasciando «% off» e un cancelletto nudo. L'approccio Extended_Pictographic ha lasciato la bandiera intatta, tolto il pollice ma non il suo tono, e lasciato i giuntori e il selettore di variante. Tre righe plausibili, tre tipi distinti di output sbagliato, e nessuna che segnali un problema.
Il quarto approccio segmenta il testo in gruppi di grafemi con Intl.Segmenter, poi scarta un gruppo intero non appena contiene un indicatore regionale, un carattere con Emoji_Presentation, o un pittogramma seguito dal selettore di variante. Sulla stessa frase ha restituito «Shipping to today — 40% off, thanks #7 »: cifre conservate, bandiera rimossa come unità, nessun giuntore orfano, nessun selettore vagante. L'unico artefatto sono i doppi spazi dove stavano le emoji, che una compattazione degli spazi risolve in una seconda passata.
Il troncamento è dove questo morde in produzione
Tagliare una stringa a N unità è di gran lunga il modo più comune di rompere le emoji, perché è il più economico da scrivere e funziona su ogni stringa di prova che a uno sviluppatore anglofono viene in mente. Prendi «Great work 👨👩👧👦 thanks», 29 unità UTF-16. Tagliare a 12, 15 o 18 fa terminare la stringa su una singola unità surrogata alta — mezzo carattere, che non è testo valido. Passalo in UTF-8 e diventa i tre byte EF BF BD, il carattere di sostituzione: il lettore vede un rombo nero. isWellFormed() restituisce false per quella stringa in JavaScript, un modo economico di prendere il bug in un test.
Il fallimento più sottile è peggiore, perché produce testo valido che significa altro. Tagliare la stessa stringa a 16 unità dà «Great work 👨👩»: la famiglia di quattro è diventata un uomo e una donna con un giuntore in mezzo, che non è una sequenza definita e quindi appare come due persone separate. Nulla è malformato. Nulla solleva un'eccezione. Il messaggio ora mostra un'immagine diversa da quella inviata.
Iterare per punto di codice con l'operatore di espansione risolve il problema del mezzo carattere e non l'altro: prendere i primi 14 punti di codice della stessa stringa dà ancora «Great work 👨👩». Solo i gruppi di grafemi ci azzeccano, perché sono l'unità che l'algoritmo di segmentazione definisce come un carattere percepito dall'utente. Prendere i primi 14 gruppi di grafemi dà «Great work 👨👩👧👦 t»: l'intera famiglia conservata come unità, che è quello che un lettore si aspetterebbe da un'anteprima di quattordici caratteri.
Contare le emoji ha lo stesso problema di toglierle
Prendi la stringa «🇵🇹 👨👩👧👦 👍🏽 ❤️ 😀». Un umano conta cinque emoji. Abbiamo interrogato sei metodi diversi e ottenuto sei risposte diverse: 27 unità UTF-16, 18 punti di codice, 9 gruppi di grafemi, 5 gruppi di grafemi non vuoti, 7 corrispondenze di \p{Extended_Pictographic}, 9 di \p{Emoji_Presentation} e 10 di \p{Emoji}. Solo una vale 5.
I conteggi per proprietà sono alti esattamente per i motivi sopra: la famiglia contribuisce con quattro pittogrammi, il pollice con uno e il suo tono con un altro sotto Emoji_Presentation, e la bandiera con due. Se il tuo prodotto applica una regola del tipo «al massimo tre emoji per pubblicazione», la regola vale solo quanto il contatore che ci sta dietro, e il contatore deve essere quello che dà ragione al lettore: i gruppi di grafemi che contengono un carattere emoji.
Che cosa fa invece togliere il non-ASCII, e perché non è lo stesso lavoro
Una scorciatoia allettante è tenere solo l'ASCII, con l'argomento che tutte le emoji stanno fuori. Ci stanno tutte, e anche gran parte del resto del mondo. Abbiamo eseguito replace(/[^\x00-\x7F]/g, "") su «Café ☕ — résumé sent 👍» e ha restituito «Caf rsum sent »: la tazza e il pollice sono spariti, e con loro la é di Café, i due accenti di résumé e la lineetta lunga. In una frase francese, spagnola, portoghese, tedesca o italiana questo distrugge parole comuni, non decorazioni.
Le due operazioni appartengono a strumenti diversi per una ragione. Togliere le emoji significa «leva le immagini e lascia stare la lingua». Togliere il non-ASCII significa «riduci questo ai 128 caratteri che un sistema vecchio sa gestire», che è un lavoro di traslitterazione con perdite reali, da scegliere consapevolmente. Ricorrere al secondo quando si voleva il primo è uno dei modi più silenziosi di rompere un prodotto multilingue.
| Emoji | Com'è costruita | Unità UTF-16 | Punti di codice | Gruppi di grafemi |
|---|---|---|---|---|
| 😀 | Un solo punto di codice, U+1F600 | 2 | 1 | 1 |
| ❤️ | Base U+2764 più selettore di variante U+FE0F | 2 | 2 | 1 |
| 1️⃣ | Cifra 1, selettore U+FE0F, keycap U+20E3 | 3 | 3 | 1 |
| 👍🏽 | Base U+1F44D più modificatore di tono U+1F3FD | 4 | 2 | 1 |
| 🇵🇹 | Due indicatori regionali, U+1F1F5 U+1F1F9 | 4 | 2 | 1 |
| 👨👩👧👦 | Quattro persone unite da tre giuntori U+200D | 11 | 7 | 1 |
| 🏴 | Bandiera nera più cinque caratteri di etichetta più un terminatore | 14 | 7 | 1 |
Domande frequenti
- Qual è la regex più corta che toglie le emoji correttamente?
- Non c'è, ed è la risposta onesta. Una regex riconosce punti di codice, e una emoji è una sequenza di punti di codice i cui confini sono definiti da un algoritmo di segmentazione, non da un pattern. Ci si avvicina con una lunga alternanza che copra pittogrammi, catene di giuntori, coppie di indicatori regionali e modificatori, ma così si reimplementa male l'algoritmo. Segmenta prima, filtra i gruppi poi: tre righe, ed è corretto.
- Perché togliendo le emoji è rimasto un carattere invisibile?
- Perché hai tolto l'immagine e non i suoi accompagnatori. I due soliti sospetti sono U+FE0F, il selettore di variante che chiede la resa come emoji, e U+200D, il giuntore a larghezza zero che collega le parti di una sequenza. Nessuno dei due è un pittogramma, quindi un filtro basato sui pittogrammi li lascia. Invisibili sullo schermo ma reali nella stringa: contano contro i limiti di caratteri, rompono i confronti di uguaglianza e sorprenderanno la prossima persona che confronta il testo.
- Perché la stessa emoji appare diversa su un altro telefono?
- La stringa porta l'identità, non il disegno. Ogni produttore distribuisce il proprio font di emoji, quindi U+1F600 è il sorriso di un disegnatore su una piattaforma e quello di un altro altrove. Quando un dispositivo non ha un glifo per una sequenza, ripiega sul disegnare le parti: una famiglia che appare come un'immagine sul tuo telefono può comparire come quattro persone separate su un apparecchio più vecchio, che è il ripiego corretto per una sequenza con giuntori, non un difetto.
- Come dovrebbe contare un'emoji un limite di caratteri?
- Conta gruppi di grafemi se il limite esiste per il lettore, e conta byte se esiste per l'archiviazione. Le due risposte differiscono di un fattore undici su una emoji di famiglia, quindi scegli quella che corrisponde al motivo del limite e scrivi accanto al campo quale sia. Ciò che non va mai fatto è applicare in silenzio una lunghezza UTF-16, perché chi digita tre bandiere sbatte su un limite di 30 caratteri a dodici caratteri visibili, senza spiegazione.
- © e ™ sono emoji?
- Per proprietà, sì e no allo stesso tempo, che è esattamente la confusione di cui parla questo articolo. Li abbiamo sondati entrambi: ciascuno ha Emoji ed Extended_Pictographic, ma nessuno ha Emoji_Presentation, quindi entrambi appaiono come testo normale a meno che un selettore di variante chieda un'immagine. Ciò significa che un filtro basato su Extended_Pictographic cancellerà il simbolo di copyright dal tuo piè di pagina. Se vuoi solo le immagini a colori, verifica Emoji_Presentation o un pittogramma seguito immediatamente da U+FE0F.
- Togliere le emoji cambia il significato di un messaggio?
- Spesso, il che è un argomento per toglierle ai bordi di un sistema e non nel mezzo. Le emoji portano tono, negazione e a volte l'intero contenuto di una risposta. Toglierle è appropriato quando la destinazione non sa mostrarle — un'esportazione in testo semplice, una colonna di database legacy, un rapporto stampato, un nome di file — e inappropriato quando il testo sarà ancora letto da una persona. Togli in uscita, tieni l'originale salvato.
Articoli che potrebbero interessarti
Tutte le guide →Strumenti correlati
Fonti
- Unicode Consortium — UTS #51: Unicode Emoji — emoji properties, ZWJ sequences, modifiers and flags
- Unicode Consortium — UAX #29: Unicode Text Segmentation — grapheme cluster boundaries
- Mozilla — MDN Web Docs — Unicode character class escapes in regular expressions
- Mozilla — MDN Web Docs — Intl.Segmenter
- Ecma International — ECMA-402: ECMAScript Internationalization API Specification
Hai notato un errore in questo articolo?