
1. 先把隱藏代碼這四個字拆開看群里隔三差五就有人轉(zhuǎn)那種長圖標題寫著微信聊天必備最新Emoji隱藏代碼大全配一張密密麻麻的表格左邊一串U1F600這樣的字符右邊一個小黃臉。底下總有人問這玩意兒怎么用我照著敲進去發(fā)出來還是一堆字母。這個問題我在幾年前做聊天內(nèi)容處理的時候也困惑過。當時要給一批聊天記錄做表情統(tǒng)計第一反應(yīng)是拿正則去匹配冒號包起來的英文單詞結(jié)果一條都匹配不上后來才反應(yīng)過來微信里根本沒有 Slack 那種:smile:自動轉(zhuǎn)換機制你發(fā)的每一個表情本質(zhì)上就是 Unicode 字符本身不存在敲代碼變圖標這回事。所以這篇東西想講清楚三件事所謂隱藏代碼究竟是哪幾種東西混在一起被叫成了同一個名字SoftBank 和 Unified 這兩套編碼到底什么關(guān)系、對照表怎么讀以及如果你想真的做到敲兩三個字母就蹦出一個表情在微信這條鏈路上有哪些能落地的辦法。適合誰看做聊天類產(chǎn)品、做文本處理、做社群運營或者單純想在聊天里少翻兩次表情面板的人都能拿走點東西。我先把最容易混淆的概念擺平不然往下看全是漿糊。1.1 三種被混稱為代碼的東西市面上流傳的Emoji代碼其實至少混了三種完全不同的東西。第一種是碼點Code Point寫作U1F600這種形式。它是 Unicode 標準給每個字符分配的編號純粹是身份證號跟微信、跟任何 App 都沒關(guān)系。你把這個編號敲進輸入框它不會變成表情因為輸入框認的是字符本身不是編號的文本表示。第二種是短代碼Shortcode長成:smile:這樣。這套東西是聊天社區(qū)早期為了在純文本環(huán)境里表達表情發(fā)明的約定服務(wù)端渲染時把短代碼替換成圖片。它是一套私有約定不同平臺各玩各的GitHub 一套、Slack 一套、Discourse 又一套。微信不支持這套自動替換。第三種是轉(zhuǎn)義序列比如 JavaScript 里的\u{1F600}、HTML 里的#128512;、CSS 里的\01F600。這是給代碼看的瀏覽器或解析器會把它還原成字符但你直接發(fā)到聊天框里對方看到的就是反斜杠加一串數(shù)字。真正能發(fā)出去變成表情的只有字符本身。所謂隱藏代碼大全絕大多數(shù)是把碼點表偽裝成了操作指南。注意看到任何教程讓你在微信輸入框輸入 U1F600直接關(guān)掉就行那玩意兒發(fā)出去就是一串 ASCII 字符。1.2 那為什么有人照著做真的成功了因為那部分人做的是復(fù)制粘貼不是輸入。碼點表和字符表往往并排放在一張圖里字符那一列是可選中的真實字符。選中、復(fù)制、粘貼到微信表情就出來了。整個過程跟代碼沒有半點關(guān)系純粹是字符搬運。還有一種情況是輸入法的自定義短語。主流輸入法都支持設(shè)置觸發(fā)碼 → 輸出內(nèi)容你把觸發(fā)碼設(shè)成xh、輸出內(nèi)容設(shè)成 之后敲xh就能出圖。這才是真正意義上的敲代碼出表情只不過這個代碼是你自己定的跟 Unicode 一點關(guān)系都沒有。我在給社群做表情包整理的時候就是靠自定義短語解決的。把三十來個高頻表情做成兩字母觸發(fā)碼聊天效率提升非常明顯比每次翻面板快太多了。1.3 這套東西的適用邊界在哪先說清楚它能干什么讓你快速輸入冷門表情、讓你在昵稱簽名里塞進特殊符號、讓你批量處理文本里的表情、讓你做聊天記錄分析。再說清楚它不能干什么。它不能突破平臺的輸入限制不能讓你發(fā)出別人看不到的隱藏消息也不能保證同一個碼點在所有人手機上長得一樣。最后這條特別重要。同一個碼點在 iOS、Android、Windows 上渲染出來的圖案可能完全不同甚至同一個系統(tǒng)的不同版本都會變。你精心挑了一個看起來最可愛的表情發(fā)出去在別人那兒可能是個方框。這個坑后面會單獨講。2. Emoji 的底層結(jié)構(gòu)一個表情不等于一個字符想搞懂對照表得先接受一個反直覺的事實你在屏幕上看到的一個表情在計算機里可能是 1 個字符也可能是 7 個、11 個字符。這個認知我第一次撞上的時候挺震驚的。當時寫一個長度校驗限制用戶輸入 20 個字測試同學(xué)輸入了四個一家四口的表情系統(tǒng)提示超長。我盯著屏幕數(shù)了半天明明是 4 個圖形怎么就超了2.1 碼點、平面與 UTF-8 / UTF-16 的換算Unicode 把字符空間分成 17 個平面每個平面 65536 個碼位。我們?nèi)粘S玫臐h字、拉丁字母在第 0 平面BMP碼點范圍 U0000 到 UFFFF。Emoji 大部分在第 1 平面從 U1F300 開始往上這一片叫 supplemental symbolsBMP 之外的區(qū)域。這個位置決定了它在不同編碼下的存儲長度編碼 (U1F600) 的表示占用UTF-8F0 9F 98 804 字節(jié)UTF-16D83D DE002 個碼元4 字節(jié)UTF-3200 01 F6 004 字節(jié)關(guān)鍵在于 UTF-16。它用 16 位表示一個碼元裝不下 U1F600所以發(fā)明了代理對Surrogate Pair用兩個 16 位碼元拼出第 1 平面之外的字符。高位代理范圍 D800–DBFF低位代理 DC00–DFFF。換算公式得記住做底層處理繞不開給定碼點 cpcp 0xFFFF high 0xD800 ((cp - 0x10000) 10) low 0xDC00 ((cp - 0x10000) 0x3FF)拿 U1F600 驗算一下cp - 0x10000 0xF600右移 10 位得0x3D加0xD800等于0xD83D低 10 位0x200加0xDC00等于0xDE00。對上了。這個知識點為什么重要因為 JavaScript 的string.length算的就是碼元數(shù)不是字符數(shù)。 在 JS 里length是 2在 Python 里len()是 1Python 3 按碼點算。同一個字符串兩門語言報出兩個數(shù)字跨語言對接的時候能坑死人。2.2 變體選擇符那個看不見的 FE0F有些字符天生可文可圖最典型的是 ?U2600。它既可以是純文本符號也可以是彩色表情。決定它長什么樣的是后面跟的那個UFE0F。U2600單獨出現(xiàn) → 系統(tǒng)按文本渲染通常是個細線條的黑色太陽U2600 UFE0F→ 強制按 emoji 渲染變成彩色UFE0F叫變體選擇符-16Variation Selector-16是個零寬字符肉眼完全看不見。這就解釋了一個高頻現(xiàn)象你從某個網(wǎng)頁復(fù)制了一個表情粘到微信里變成個黑白小符號怎么調(diào)都調(diào)不回來。原因就是復(fù)制的時候那個 FE0F 被丟了。反過來還有UFE0EVS-15作用是強制文本渲染。想發(fā)個看起來不像表情的表情就用它。數(shù)字鍵帽那套也是同一個道理1?? U0031 UFE0F U20E3 數(shù)字1變體選擇包圍鍵帽三個碼點渲染成一個圖形。你要是把中間那個 FE0F 刪掉某些系統(tǒng)就只顯示一個光禿禿的 1。提示做文本清洗的時候千萬別無腦過濾不可見字符。FE0F、ZWJ、膚色修飾符都屬于這一類刪了表情就散架。2.3 ZWJ 與膚色修飾符長度爆炸的元兇U200D叫零寬連接符ZWJ, Zero Width Joiner作用是告訴渲染引擎把前后兩個表情粘成一個。這是 emoji 組合的核心機制。一家四口那個表情 ZWJ ZWJ ZWJ 拆開算4 個人物表情每個是 2 個 UTF-16 碼元共 8 個3 個 ZWJ每個 1 個碼元共 3 個。合計11 個碼元7 個碼點但用戶眼里只有1 個字符。這種多個碼點組成一個視覺單位的東西專業(yè)叫法是字素簇Grapheme Cluster。判斷長度的正確姿勢是按字素簇算不是按碼點更不是按碼元。膚色修飾符是另一套機制。U1F3FB到U1F3FF五個碼點對應(yīng)從淺到深的五檔膚色跟在人物表情后面直接生效 U1F466 → 默認黃色小孩 U1F466 U1F3FB → 淺膚色 U1F466 U1F3FF → 深膚色注意這是緊跟不是 ZWJ 連接。寫錯了沒用會渲染成兩個獨立字符。這幾種結(jié)構(gòu)疊在一起就出現(xiàn)了一個經(jīng)典的長尾問題你以為限制了 20 個字實際上用戶輸入 4 個復(fù)合表情就超了你以為數(shù)據(jù)庫字段長度夠?qū)嶋H上一條記錄光表情就占掉幾十個碼元。3. SoftBank、Unified 與各平臺編碼的對照關(guān)系現(xiàn)在進入正題。為什么會有 SoftBank 這個東西3.1 私有區(qū)編碼是怎么來的時間倒回功能機時代。那會兒各家運營商各自搞了一套表情符號在自己的網(wǎng)絡(luò)和定制機之間傳。這些符號在 Unicode 標準里根本不存在于是被塞進了私有使用區(qū)PUA, Private Use Area也就是 UE000–UF8FF 這一片誰愛用誰用、標準不管的區(qū)域。SoftBank 的映射就是這套歷史產(chǎn)物碼位集中在 UE001–UE05A、UE101–UE15A、UE201–UE253 等幾個區(qū)間。同期還有另外兩家運營商也各搞了一套碼位互不相同。后來 Unicode 官方開始收編表情把它們統(tǒng)一分配到了 U1F300 往上那一片正式碼位這套統(tǒng)一后的編碼就叫Unified。收編的過程中官方做了映射表把各家 PUA 碼位對應(yīng)到新的 Unified 碼位。所以兩者的關(guān)系是SoftBank 是歷史遺留的私有編碼Unified 是現(xiàn)在的標準編碼對照表就是一張舊地址 → 新地址的遷移表。有意思的是這個遷移不是一一對應(yīng)的。SoftBank 那套總共幾百個表情而現(xiàn)在的 Unicode 表情已經(jīng)超過三千個含各種組合。所以會出現(xiàn)一個 SoftBank 碼位對一個 Unified 碼位最常見多個 SoftBank 碼位對同一個 Unified歷史重復(fù)大量 Unified 碼位沒有SoftBank 對應(yīng)新增的表情3.2 對照表長什么樣下面這張表是結(jié)構(gòu)示意列了我印象最深的幾個映射方便你理解字段關(guān)系。完整的映射有幾百行從開源數(shù)據(jù)倉庫拉更靠譜。SoftBank (PUA)Unified (Unicode)字符名稱UE001U1F466boyUE002U1F467girlUE003U1F468manUE004U1F469womanUE005U1F46AfamilyUE04AU2600?black sun with raysUE04BU2601?cloudUE04CU1F319crescent moon讀這張表有個細節(jié)要注意U2600和U1F319都在 BMP 或者第 1 平面跟前面那些 U1F4xx 混在一起看不出規(guī)律。這是因為 Unicode 收編的時候是按語義分區(qū)不是按來源分區(qū)。另一件事SoftBank 碼位落在 PUA意味著它沒有標準字形。同一個 UE001在舊設(shè)備上可能顯示成小男孩在現(xiàn)在的設(shè)備上大概率是個方塊或者問號因為現(xiàn)代字體根本不認這個地址。這就是為什么老聊天記錄遷移到新系統(tǒng)里表情會集體變成豆腐塊。3.3 自己生成對照表比抄表靠譜網(wǎng)上流傳的對照表質(zhì)量參差不齊我見過至少三個版本在 UE04C 這一行對不上。與其抄不如自己拉官方數(shù)據(jù)生成。Unicode 官方在 emoji 數(shù)據(jù)倉庫里放了幾個關(guān)鍵文件文件名內(nèi)容用途emoji-data.txt所有 emoji 碼點及屬性判斷某碼點是不是 emojiemoji-test.txt完整序列及限定狀態(tài)生成完整表情清單emoji-sequences.txt組合序列鍵帽、旗幟等處理多碼點組合emoji-zwj-sequences.txtZWJ 組合序列處理家庭、職業(yè)等復(fù)合表情emoji-test.txt的行格式是這樣的1F600 ; fully-qualified # E1.0 grinning face四個字段用分號和井號隔開碼點序列、限定狀態(tài)、字符本身、引入版本和名稱。限定狀態(tài)有四種取值fully-qualified是完整形式minimally-qualified是省略了 FE0F 的簡化形式unqualified是完全省略component表示這是個組件比如膚色修飾符不能單獨用。做輸入聯(lián)想、做表情搜索都從這個文件入手最穩(wěn)。至于 SoftBank 那部分映射不在上面這幾個文件里得單獨找社區(qū)維護的對照數(shù)據(jù)。我這里給個建議如果你的場景是新建系統(tǒng)壓根不需要 SoftBank。它只在處理歷史數(shù)據(jù)遷移、解析老設(shè)備日志的時候才用得上。4. 動手從數(shù)據(jù)整理到微信端落地光講原理沒意思下面是我實際搭過的一套流程從數(shù)據(jù)源到最終在聊天里用起來每一步都能復(fù)現(xiàn)。4.1 第一步把數(shù)據(jù)源拉下來清洗先從官方倉庫拿到emoji-test.txt然后解析成結(jié)構(gòu)化數(shù)據(jù)。Python 處理這類文本特別順手import re PATTERN re.compile( r^(?Pcodes[0-9A-F ])\s*;\s*(?Pstatus\S)\s*#\s*(?Pchar\S)\s*E(?Pversion[\d.])\s*(?Pname.)$ ) def parse_emoji_test(path): items [] with open(path, encodingutf-8) as f: for line in f: line line.strip() if not line or line.startswith(#): continue m PATTERN.match(line) if not m: continue codes [int(c, 16) for c in m.group(codes).split()] items.append({ codes: codes, char: .join(chr(c) for c in codes), status: m.group(status), version: m.group(version), name: m.group(name).strip(), }) return items跑完之后你會拿到幾千條記錄。接下來做個篩選只保留fully-qualified。其他狀態(tài)在輸入框里發(fā)出去多數(shù)客戶端會自動補全成完整形式你按簡化形式存反而容易出問題。這一步我踩過的坑是沒處理版本。E1.0到E15.0跨度十幾年如果你的目標用戶里有大量老舊安卓機把 E13.0 之后的新表情推給他們顯示出來就是空白。合理做法是按版本打個標記前端根據(jù) UA 或者系統(tǒng)版本做降級。4.2 第二步編碼轉(zhuǎn)換的核心邏輯SoftBank 轉(zhuǎn) Unified本質(zhì)就是查表替換。邏輯簡單到有點無聊SOFTBANK_TO_UNIFIED { 0xE001: [0x1F466], 0xE002: [0x1F467], 0xE003: [0x1F468], 0xE004: [0x1F469], 0xE005: [0x1F46A], 0xE04A: [0x2600], 0xE04B: [0x2601], } def softbank_to_unified(text: str) - str: out [] for ch in text: cp ord(ch) targets SOFTBANK_TO_UNIFIED.get(cp) if targets: out.append(.join(chr(c) for c in targets)) else: out.append(ch) return .join(out)注意 value 用的是列表而不是單個整數(shù)。原因前面提過少數(shù) SoftBank 碼位會映射到多個 Unicode 碼點組成的序列比如某些帶修飾的組合。用列表能兼容兩種情況以后補數(shù)據(jù)不用改函數(shù)簽名。反向轉(zhuǎn)換就麻煩多了因為映射是多對一的一個 Unified 碼位可能對應(yīng)多個 SoftBank 碼位也可能一個都沒有。穩(wěn)妥做法是給每個 Unified 碼位維護一個候選列表轉(zhuǎn)換時取第一個同時記錄無對應(yīng)的情況。def unified_to_softbank(text: str, reverse_map: dict) - str: out [] for ch in text: cp ord(ch) candidates reverse_map.get(cp) if candidates: out.append(chr(candidates[0])) else: # 沒有對應(yīng)原樣保留或替換成占位符 out.append(ch) return .join(out)實際跑歷史數(shù)據(jù)的時候無對應(yīng)的比例會高得嚇人。我處理過一批十年前的聊天記錄SoftBank 表情占比不到 15%剩下全是后來新增的 Unified 表情。所以如果你的遷移方向是老 → 新工作量很小如果是新 → 老基本沒法無損。4.3 第三步在微信里真正用起來回到最開始的問題。想在微信里快速發(fā)冷門表情有下面幾條路按我實際體驗的順手程度排序。路子一輸入法自定義短語。這是最接近敲代碼出圖的方案。進輸入法設(shè)置找到自定義短語或快捷輸入新增一條觸發(fā)碼填xh內(nèi)容填 。保存后在任何輸入框里敲xh候選欄第一個就是這個表情。我自己的碼表是這么設(shè)計的兩個字母第一個字母表示大類x笑、k哭、z贊、a愛第二個字母是細分。三十來條覆蓋了日常 95% 的場景敲起來比翻面板快。這套碼表跟著輸入法賬號走換手機也能同步。路子二系統(tǒng)級文本替換。iOS 在設(shè)置 → 通用 → 鍵盤 → 文本替換里安卓各家在語言與輸入法里都有類似功能。效果跟輸入法短語差不多區(qū)別是系統(tǒng)級替換對所有 App 生效包括那些不吃輸入法候選欄的場景。缺點是改起來麻煩得進設(shè)置一層層翻。路子三微信自定義表情。把常用表情存成自定義表情會進到表情面板的收藏Tab位置固定翻起來比在幾百個默認表情里找快。適合那些一天用十次的高頻表情。路子四復(fù)制粘貼。最原始但對付一次性需求很好用。從系統(tǒng)字符面板iOS 長按地球鍵、macOS 的 CmdCtrlSpace、Windows 的 Win句號里選比在網(wǎng)上找對照表靠譜得多。提示別信輸入特定字符組合會觸發(fā)彩蛋表情這類說法。真有這種機制也早就在版本更新里被修掉了。4.4 第四步長度計算與截斷繞不開的坑前面鋪墊了那么多碼點、碼元、字素簇這里就是用武之地。假設(shè)你要做一條最多 50 字的輸入限制。用len()算碼點數(shù)用戶輸入乘以 12 組碼點數(shù)是 84提示超長但用戶眼里只有 12 個圖形主觀感受是我才打了 12 個。這種體驗落差會直接被投訴。正確做法是按字素簇算import regex def grapheme_len(text: str) - int: return len(regex.findall(r\X, text)) print(grapheme_len()) # 1 print(len()) # 7regex這個第三方庫支持\X元字符專門匹配字素簇。標準庫re不支持硬用會出錯。JavaScript 這邊稍微麻煩點得用Intl.Segmenterconst seg new Intl.Segmenter(zh, { granularity: grapheme }); const s ; console.log([...seg.segment(s)].length); // 1 console.log(s.length); // 11Intl.Segmenter在主流瀏覽器和 Node 16 都支持老環(huán)境得準備 polyfill。還有個更隱蔽的坑截斷。按碼點截斷很容易把代理對劈成兩半產(chǎn)生非法字符。比如把的D83D留下、DE00砍掉結(jié)果是一個無法渲染的孤立代理。這個字符串存進數(shù)據(jù)庫、傳給別人可能直接導(dǎo)致對方客戶端報錯。穩(wěn)妥的截斷方式是先按字素簇切分再取前 N 個最后拼回去。慢一點但不會出事。5. 常見問題與排查實錄這一節(jié)全是踩過的坑按出現(xiàn)頻率排序。5.1 顯示成方塊、問號、空白這是最高頻的問題原因至少四種得逐個排。原因一碼點超出接收端支持范圍。對方系統(tǒng)版本老字體里沒有這個字形就顯示方塊俗稱豆腐塊。Unicode 15.0 之后新增的表情在兩三年前的設(shè)備上基本都不認。判斷方法把同一個表情發(fā)給不同系統(tǒng)的設(shè)備如果只有部分出問題就是這個原因。沒解只能降級用老表情。原因二變體選擇符丟失。前面講過?少了UFE0F就從彩色變黑白1??少了U20E3就退化成普通數(shù)字。這類問題肉眼看著沒壞但不對容易被忽略。原因三PUA 字符沒做轉(zhuǎn)換。老數(shù)據(jù)里殘留的UE001這類私有區(qū)碼位現(xiàn)代字體一律不認全是方塊。排查方法很簡單把字符串按碼點拆開看有沒有落在UE000–UF8FF區(qū)間的有就說明需要跑一遍轉(zhuǎn)換。原因四編碼不匹配。UTF-8 字節(jié)流被當成 Latin-1 解碼會出現(xiàn)e和一堆亂碼。這個特征太明顯一般不會認錯。5.2 數(shù)據(jù)庫存儲字符集是第一道關(guān)MySQL 用utf8字符集注意不是utf8mb4存 emoji會直接報錯或者存成????。因為 MySQL 的utf8實際是utf8mb3最多 3 字節(jié)而第 1 平面的 emoji 需要 4 字節(jié)。改法ALTER TABLE chat_messages CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;改完之后還有兩個連帶問題。一是索引長度。早期 MySQL 版本單列索引最長 767 字節(jié)utf8mb4一個字符占 4 字節(jié)所以 varchar 索引最多 191 個字符。這就是那個著名的191 索引長度問題的來源?,F(xiàn)在主流版本默認 3072 字節(jié)基本不用管了但如果接手老庫還是可能撞上。二是排序和比較。不同 collation 對 emoji 的處理不一樣做按表情聚合這類需求時utf8mb4_unicode_ci和utf8mb4_0900_ai_ci的結(jié)果可能不同。我一般統(tǒng)一用后者MySQL 8.0 默認值行為更符合直覺。5.3 問題速查表把上面這些整理成一張表出問題時對著查現(xiàn)象最可能原因排查方法處理方式發(fā)出去是方框接收端無該字形換設(shè)備交叉驗證降級用低版本表情黑白與彩色不一致缺 FE0F打印碼點十六進制補上 UFE0F鍵帽不對勁缺 20E3 或 FE0F同上補全三碼點序列數(shù)據(jù)庫存成問號字符集非 utf8mb4SHOW CREATE TABLE改字符集截斷后整條消息異常代理對被劈開檢查孤立代理項按字素簇截斷長度校驗誤判按碼元計數(shù)對比碼元數(shù)與字素簇數(shù)改按字素簇算老記錄全是方塊PUA 未轉(zhuǎn)換查碼點是否在 PUA跑 SoftBank 轉(zhuǎn)換5.4 我自己踩得最慘的一次說個具體的。早期做聊天記錄導(dǎo)出我寫了個清理不可見字符的函數(shù)用的是白名單之外一律過濾的邏輯。上線之后用戶反饋所有一家三口類的復(fù)合表情都變成了三個人分開站著中間還多倆空格。原因就是把U200D當成了不可見字符給刪了。ZWJ 確實是零寬的但它承擔(dān)的是連接語義刪掉之后組合就散了。后來我把白名單改成了只過濾 U0000–U0008、U000B–U001F 這些真正的控制字符其他一律保留。另外加了一條規(guī)則任何對文本的壓縮、去重、清洗都要在字素簇層面做不能碰單個碼點。這件事之后我養(yǎng)成了一個習(xí)慣凡是處理文本的代碼測試用例里必帶三種數(shù)據(jù)——一個普通 emoji、一個 ZWJ 復(fù)合表情、一個帶膚色的表情。三個都過了才算過。還有一個偏門的經(jīng)驗微信的昵稱和簽名對表情的支持比聊天框保守。有些在聊天里正常顯示的表情放進昵稱會顯示成問號或者被截斷。我一般建議用 Unicode 6.0 之前的老表情兼容性最穩(wěn)。6. 關(guān)于對照表最后說幾句實在話我把Emoji隱藏代碼這件事拆到現(xiàn)在結(jié)論其實有點掃興真正有用的不是那張表而是背后的數(shù)據(jù)處理能力。對照表是一次性消費品抄完就完事了而且大概率抄來的版本有錯。但只要你理解了碼點、代理對、字素簇這三層結(jié)構(gòu)遇到任何表情顯示異常長度計算不對截斷出亂碼的問題都能自己定位。至于微信這條鏈路最實用的技巧說出來可能讓人失望——配好輸入法的自定義短語比任何對照表都管用。我自己的碼表用了三年中間只調(diào)整過兩次一次是加了幾個新的一次是刪掉了幾個總打錯重碼的。這套東西一旦配好聊天效率的提升是實打?qū)嵉谋仁詹匾话購垖φ臻L圖強。如果還想往下擴展有兩個方向值得琢磨。一是把表情按語義標簽重建索引比如把所有情緒-正向的表情歸一類做搜索的時候按情緒找比按名字找符合直覺。二是做版本適配層根據(jù)客戶端版本動態(tài)決定用哪套表情這個在跨端產(chǎn)品里遲早要面對。這兩個話題都能單獨寫一篇今天就不展開了。