EmojiEmoji
Từ tiếng Nhật (絵文字) có nghĩa là 'ký tự hình ảnh' — các ký hiệu đồ họa nhỏ dùng trong giao tiếp kỹ thuật số để diễn đạt ý tưởng, cảm xúc và sự vật. Detection in Text Strings
Detecting whether a string contains emojis—and extracting them accurately—is harder than it looks. The Unicode standardUnicode Standard
Hệ thống mã hóa ký tự đầy đủ do Unicode Consortium duy trì, định nghĩa các ký tự, thuộc tính, thuật toán và dạng mã hóa. has grown to include over 3,700 emoji characters spread across multiple code point ranges, with new ones added every year. A naive approach using a fixed range check will miss most of them.
This guide covers the algorithms, UnicodeUnicode
Tiêu chuẩn mã hóa ký tự phổ quát gán một số duy nhất cho mỗi ký tự trong tất cả hệ thống chữ viết và bộ ký hiệu, bao gồm cả emoji. properties, and production-ready libraries you need to detect emojis reliably.
Why Simple Range Checks Fail
A common first attempt is checking whether a code point falls in the range U+1F600–U+1F64F (Emoticons block). This catches classics like 😀, 😂, and 😎, but misses:
- Basic emoji: ©️ (U+00A9), ® (U+00AE), ™️ (U+2122) — in the Latin Extended range
- Dingbats: ✅ (U+2705), ❌ (U+274C) — in the Dingbats block
- Enclosed alphanumerics: 🅰️, 🅱️
- ZWJZero Width Joiner (ZWJ)
Ký tự Unicode vô hình (U+200D) dùng để ghép nhiều emoji thành một emoji tổng hợp, chẳng hạn kết hợp người và vật thể thành emoji nghề nghiệp. sequences: 👨💻 (man technologist) — multiple code points joined by U+200D - Keycap sequences: 1️⃣ — digit + variation selectorVariation Selector (VS)
Các ký tự Unicode (VS-15 U+FE0E và VS-16 U+FE0F) xác định xem một ký tự được hiển thị dưới dạng văn bản (đơn sắc) hay emoji (có màu). + combining enclosing keycap - Flag sequences: 🇺🇸 — pairs of Regional IndicatorRegional Indicator (RI)
Các chữ cái Unicode ghép đôi (U+1F1E6 đến U+1F1FF) tạo thành emoji cờ quốc gia khi kết hợp theo mã ISO 3166-1 alpha-2. letters
The only reliable approach is to use the official Unicode emoji property data.
The Unicode Emoji Properties Approach
Unicode defines several properties relevant to emoji detection, published in emoji-data.txt from the Unicode Character Database (UCD):
| Property | Meaning |
|---|---|
Emoji |
The code point is an emoji |
Emoji_Presentation |
Displayed as emoji by default (not text) |
Emoji_Modifier |
A skin tone modifierSkin Tone Modifier Năm ký tự điều chỉnh Unicode dựa trên thang Fitzpatrick, thay đổi màu da của emoji người (từ U+1F3FB đến U+1F3FF). (🏻–🏿) |
Emoji_Modifier_Base |
Can be modified by a skin tone modifier |
Emoji_Component |
Used in emoji sequences (ZWJ, keycap, etc.) |
Extended_Pictographic |
Broader set including reserved ranges |
For most detection tasks you want Extended_Pictographic, which includes current emoji plus code points reserved for future emoji assignments.
Detection in Python
Using the emoji Library
The emoji library maintains an up-to-date Unicode dataset:
import emoji
text = "Hello 👋 world! Check this out 🚀"
# Check if string contains any emoji
has_emoji = emoji.emoji_count(text) > 0
print(has_emoji) # True
# Count emojis
count = emoji.emoji_count(text)
print(count) # 2
# Extract emoji with positions
for item in emoji.emoji_list(text):
print(item)
# {'match_start': 6, 'match_end': 7, 'emoji': '👋'}
# {'match_start': 26, 'match_end': 27, 'emoji': '🚀'}
# Replace emojis
clean = emoji.replace_emoji(text, replace="")
print(clean) # "Hello world! Check this out "
Using the regex Module with Unicode Properties
The third-party regex module (not the built-in re) supports Unicode properties:
import regex
# Match any Extended_Pictographic character or emoji sequenceEmoji Sequence
Một tập hợp có thứ tự gồm một hoặc nhiều điểm mã Unicode cùng nhau đại diện cho một ký tự emoji duy nhất.
EMOJI_PATTERN = regex.compile(
r'\p{Extended_Pictographic}'
r'(?:\uFE0F)?' # optional variation selector-16
r'(?:\u20E3)?' # optional combining enclosing keycap
r'(?:\uFE0F\u20E3)?' # keycap sequenceKeycap Sequence
Một chuỗi emoji được tạo thành từ chữ số hoặc ký hiệu, theo sau là VS-16 (U+FE0F) và ký tự keycap bao quanh (U+20E3).
r'(?:\u200D\p{Extended_Pictographic}(?:\uFE0F)?)*' # ZWJ sequences
r'(?:[\U0001F1E0-\U0001F1FF]{2})?', # flag sequences
regex.UNICODE
)
text = "Deploying 🚀 to production 👨💻 — fingers crossed 🤞🏽"
matches = EMOJI_PATTERN.findall(text)
print(matches) # ['🚀', '👨💻', '🤞🏽']
Pure stdlib with unicodedata
For simpler cases without extra dependencies, check the unicodedata category:
import unicodedata
def contains_emoji_simple(text: str) -> bool:
for char in text:
cat = unicodedata.category(char)
# So (Symbol, other) covers many but not all emoji
if cat == "So":
return True
return False
This is fast but incomplete — it misses many emoji that fall in other categories.
Detection in JavaScript
Using the emoji-regex Package
import emojiRegex from 'emoji-regex';
const regex = emojiRegex();
const text = "Meeting at 3pm 📅 — bring your laptop 💻";
// Test for presence
console.log(regex.test(text)); // true
// Extract all emojis
const matches = [...text.matchAll(regex)];
matches.forEach(m => {
console.log(`Found: ${m[0]} at index ${m.index}`);
});
// Found: 📅 at index 15
// Found: 💻 at index 38
// Count
const count = [...text.matchAll(regex)].length;
console.log(count); // 2
Note that emoji-regex is generated directly from Unicode data, so it stays accurate across emoji versions.
Native Unicode Property Escapes (ES2018+)
Modern JavaScript engines support \p{} in regex with the u flag:
// Requires Node.js 10+ or modern browsers
const emojiRx = /\p{Emoji}/u;
const extPictoRx = /\p{Extended_Pictographic}/u;
console.log(emojiRx.test("Hello 🌍")); // true
console.log(extPictoRx.test("No emoji here")); // false
// Extract using matchAll
const text = "Status: ✅ Build passed, 🔴 Tests failed";
const allEmoji = [...text.matchAll(/\p{Extended_Pictographic}/gu)];
console.log(allEmoji.map(m => m[0])); // ['✅', '🔴']
Detection in Go
package main
import (
"fmt"
"unicode"
"golang.org/x/text/unicode/rangetable"
)
// Basic check using unicode.Is
func containsEmoji(s string) bool {
for _, r := range s {
if unicode.Is(unicode.So, r) || // Symbol, other
(r >= 0x1F600 && r <= 0x1FFFF) || // Supplemental symbols
(r >= 0x2600 && r <= 0x27BF) { // Misc symbols
return true
}
}
return false
}
func main() {
texts := []string{
"Hello world",
"Rocket 🚀 launched",
"©️ Copyright symbol",
}
for _, t := range texts {
fmt.Printf("%q → %v\n", t, containsEmoji(t))
}
}
For production Go code, consider the github.com/rivo/uniseg package, which handles grapheme cluster segmentation correctly and can identify emoji clusters.
Handling Edge Cases
Variation Selectors
Many emoji have both a text (VS15, U+FE0E) and emoji (VS16, U+FE0F) presentation. The digit ☎ can appear as ☎︎ (text) or ☎️ (emoji). Your detection should account for the variation selector:
phone_text = "\u260E\uFE0E" # ☎︎ text presentationText Presentation
Cách hiển thị một ký tự dưới dạng ký hiệu văn bản đơn sắc, hoặc theo mặc định hoặc khi áp dụng Variation Selector-15.
phone_emoji = "\u260E\uFE0F" # ☎️ emoji presentationEmoji Presentation
Cách hiển thị mặc định của một ký tự dưới dạng glyph emoji có màu, hoặc vốn có hoặc được kích hoạt bởi Variation Selector-16.
import emoji
print(emoji.emoji_count(phone_text)) # 0
print(emoji.emoji_count(phone_emoji)) # 1
ZWJ Sequences
👨💻 is a single grapheme cluster composed of 👨 + ZWJ (U+200D) + 💻. When counting or extracting emoji, treat ZWJ sequences as one unit. Libraries like emoji (Python) and emoji-regex (JS) handle this automatically.
Regional Indicator Flags
Country flags like 🇩🇪 consist of two Regional Indicator letters (U+1F1E6–U+1F1FF). They are only valid in pairs. A single 🇩 without a following 🇪 is not a flag.
Performance Considerations
For high-throughput text processing:
- Pre-compile your regex — do it once at module load, not per call
- Short-circuit on ASCII — if all bytes are < 128, there are no emoji (they are all non-ASCII)
- Use a library — regex-based approaches with proper Unicode support are faster than custom range tables you maintain yourself
def fast_has_emoji(text: str) -> bool:
# Short-circuit: emoji require non-ASCII bytes in UTF-8UTF-8
Kiểu mã hóa Unicode có chiều rộng thay đổi, dùng từ 1 đến 4 byte cho mỗi ký tự, thống trị trên web (98%+ website sử dụng).
if text.isascii():
return False
return emoji.emoji_count(text) > 0
Explore More on EmojiFYI
- Analyze emoji sequences in detail: Sequence Analyzer
- Browse the emoji glossary: Glossary
- Integrate emoji data in your app: API Reference
- Find specific emoji fast: Search