Vai al contenuto
Allin

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

Daniel OkonkwoSviluppatore front-end e redattore Tech presso Allin

Performance web · Formati di file

Verificato su 5 fonti

Vedi il profilo
In breve

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.

Unità UTF-16
Una emoji visibile, misurata in tre modi — Node 26, Intl.Segmenter con granularity "grapheme"
EmojiCom'è costruitaUnità UTF-16Punti di codiceGruppi di grafemi
😀Un solo punto di codice, U+1F600211
❤️Base U+2764 più selettore di variante U+FE0F221
1️⃣Cifra 1, selettore U+FE0F, keycap U+20E3331
👍🏽Base U+1F44D più modificatore di tono U+1F3FD421
🇵🇹Due indicatori regionali, U+1F1F5 U+1F1F9421
👨‍👩‍👧‍👦Quattro persone unite da tre giuntori U+200D1171
🏴󠁧󠁢󠁳󠁣󠁴󠁿Bandiera nera più cinque caratteri di etichetta più un terminatore1471
Rimuovi emojiRimuovi tutti gli emoji e i pittogrammi dal testo.Prova lo strumento

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
GuidaFormattare i numeri per sei lingue: separatori, valuta e il ritorno al valore1.234,56 e 1,234.56 sono lo stesso numero, e confonderli cambia il valore che un lettore legge. Abbiamo eseguito Intl.NumberFormat per tutte e sei le lingue del sito e stampato ogni separatore — compreso quello invisibile usato dal francese — e poi misurato perché parseFloat non può disfare nulla di tutto ciò.SpiegazioneContare le parole è ambiguo, e ogni strumento risponde in modo diversoUn conteggio di parole è una definizione, non una misura. Abbiamo contato lo stesso paragrafo in quattro modi ottenendo 25, 28, 33 e 38; poi abbiamo contato 50.000 caratteri di prosa ordinaria ottenendo accordo entro il 4,5 %. Lo scarto dipende interamente da composti, cifre e URL.TutorialFiltrare righe secondo uno schema, senza riga di comandoQuesto è grep per chi non usa grep, con una differenza importante: la ricerca è una sottostringa letterale, quindi una vera espressione regolare restituisce un riquadro vuoto e nessun errore. Ogni affermazione è stata verificata eseguendo lo strumento.SpiegazioneDove una riga può andare a capo: l'algoritmo Unicode dietro ogni paragrafo mandato a capo«Andare a capo sugli spazi» fallisce nella maggior parte dei sistemi di scrittura. UAX #14 assegna a ogni carattere una classe di interruzione di riga; abbiamo cercato le nostre in Unicode 17.0.0 ed eseguito un'implementazione conforme su spazi unificatori, trattini morbidi, spazi a larghezza zero, URL, giapponese e thai.SpiegazioneTogliere gli accenti rompe la ricerca — finché non lo fai da entrambi i latiRipiegare i segni diacritici è un passaggio di normalizzazione, e la normalizzazione funziona solo se la stessa funzione gira sull'indice e sulla query. NFC contro NFD con i punti di codice in chiaro, e le lettere — ø, ł, ß, œ, ı — che escono dal ripiegamento intatte.GuidaConvertire fra formati di elenco senza perdere dati: le regole di virgolettatura che nessuno leggeTrasformare un elenco a capo in un elenco a virgole è banale finché un elemento non contiene una virgola. Le regole di virgolettatura della RFC 4180, perché un campo CSV può contenere un a capo, perché i fogli di calcolo europei usano il punto e virgola, e cosa fa un elemento vuoto all'andata e ritorno — ogni caso eseguito e stampato.

Strumenti correlati

Fonti

Hai notato un errore in questo articolo?