← All Tools

Are Digits Technically Emoji? — Intl.Segmenter and the Emoji False-Positive Problem

Guide · Last verified Aug 27, 2026

"Just strip the emoji" sounds like a simple request, but there's a trap hiding inside the Unicode standard: the digits 0-9, along with # and *, are officially classified as characters that carry the "Emoji" property. An emoji-removal tool built without knowing this will false-positive on ordinary digits and symbols sitting right in the middle of normal text. Here's why that classification exists, and how to process text correctly to avoid the false positives.

1. Why digits are classified as "emoji": they're the raw material for keycap emoji

You've probably seen "keycap" emoji like 0️⃣ through 9️⃣, #️⃣, and *️⃣. These aren't actually single characters — each is a composite sequence built from a base character (a digit 0-9, #, or *) followed by a variation selector (U+FE0F) and a combining enclosing keycap character (U+20E3). The Unicode standard gives the Emoji property to the base character itself, since it's the "raw material" that can form this sequence. That means even the plain digit "5" on its own is flagged as Emoji=Yes in the Unicode database.

Property comparison: the character "5" (U+0035) doesn't carry the Emoji_Presentation property, but it does carry the Emoji property itself — Yes. "5️⃣", on the other hand, is a sequence of three combined code points: U+0035 + U+FE0F + U+20E3. A regex like /\p{Emoji}/u matches on the base character "5" alone regardless of whether the other two code points are attached, so naively applying just that regex will misidentify an ordinary "5" sitting in body text as an emoji and delete it.

2. Why naive filtering is risky

Many emoji-removal implementations scan text with a single /\p{Emoji}/u regex and delete anything that matches. Even in text with zero actual emoji — pure Korean or English prose — any digits or symbols inside phrases like "in 2024," "item #3," or "#1" are at risk of being wiped out along with everything else. In text where digits and # show up constantly — prices, phone numbers, rankings, hashtags — this false positive quietly corrupts the output, and it's hard to catch because nothing errors out; characters just silently disappear.

3. The correct approach: only remove combining characters when they're actually attached

A sequence should only be treated as a real keycap emoji when a variation selector (U+FE0F) or combining keycap (U+20E3) is actually attached after the base character. When the base character appears alone, it should be left as plain text. The Emoji Remover handles this by first splitting text into human-perceived "character units" (grapheme clusters) using the Intl.Segmenter API, then testing each unit against the Unicode Emoji property regex — while also checking each unit against /^[0-9#*]$/ to precisely exclude "standalone digit/#/*" characters from being flagged as emoji. In other words, a digit, #, or * with no combining character attached is excluded from removal from the start, while an actual keycap emoji sequence (with the combining character attached) is recognized as a single grapheme and removed as a whole.

InputWhat it actually isResult
"item 3"Standalone digit (no combining character)Preserved
"item 3️⃣"Keycap emoji sequence3️⃣ is removed entirely
"#1 ranking"Standalone # (no combining character)Preserved

4. Another reason grapheme-cluster processing matters

Preventing this false positive is part of a bigger reason text needs to be handled by grapheme cluster rather than one code point at a time. A family emoji like 👨‍👩‍👧 is also, under the hood, a composite string made of three separate emoji (man, woman, girl) joined by a Zero Width Joiner (ZWJ). Processing code point by code point strips only part of it and leaves broken fragments behind. Skin-tone variants (👍🏽) and flag emoji (🇰🇷) are the same story — multiple code points bundled into a single grapheme. Intl.Segmenter groups all of these cases exactly the way a human eye perceives them, which is what makes it possible to solve both the digit false-positive problem and complete removal of compound emoji at the same time.

5. Other text-processing tasks with the same underlying problem

Ignoring the difference between code points and graphemes in emoji/special-character handling causes the same kind of issue in other text tools — character counting, string truncation, normalization, and more. If you want to count characters accurately, try the Text Statistics tool; if you just want to verify emoji counts separately, the Emoji Counter can check that. And if you want to inspect exactly what's happening at the Unicode code-point level, the Unicode Inspector is useful for that.

FAQ

Q. Is there a real-world case where digits carrying the Emoji property actually causes problems?

Yes. A script that filters text using only the /\p{Emoji}/u regex can silently strip digits and # from things like prices ("$1000"), phone numbers, or rankings ("#1") — a real bug, not a theoretical one.

Q. Do ordinary symbols like ★ or ✓ have the same problem?

No. Symbols like ★ or ✓ never carried the Unicode Emoji property in the first place, so they don't match that regex. The false-positive problem is limited to the specific characters that serve as raw material for keycap emoji: digits 0-9, #, and *.

Q. Can this be solved without Intl.Segmenter?

Preventing the standalone-digit false positive alone can be done with a simple regex exception. But safely handling ZWJ compound emoji or skin-tone/flag emoji as whole units in practice requires splitting text into grapheme clusters.

Q. What if I actually want to remove keycap emoji themselves?

A sequence where a variation selector or combining keycap character is actually attached is correctly identified as an emoji and gets removed. Only the standalone digit/#/* characters are excepted — real keycap emoji sequences are still removed as normal.