)
1. 先把“隱藏代碼”這件事說透表情在聊天框里到底是什么微信聊天里那些“隱藏代碼”說白了就是一段段看不見或半可見的 Unicode 字符序列。你按下發(fā)送鍵的那一刻發(fā)出去的并不是一張圖片而是一串碼位Code Point對方手機收到這串?dāng)?shù)字后用自己系統(tǒng)里的字體把它“畫”成一個表情。所以同一個表情安卓和 iOS 畫出來胖瘦不一樣老設(shè)備干脆畫成一個方塊——這不是微信的鍋是字體渲染的差異。這篇東西想解決三個問題搞清楚這些碼位怎么讀、SoftBank 那套老編碼和現(xiàn)在的 Unified統(tǒng)一碼怎么對上、以及怎么在自己的輸入習(xí)慣里把它變成可以天天用的東西。SoftBank 對照表和Unified 碼位是全文的兩條主線前者解決“老資料看不懂”后者解決“新表情怎么自己造”。適合三類人看經(jīng)常在群里被問“這個表情你哪來的”的重度聊天用戶、需要做文案或社群運營的內(nèi)容崗、以及想在自己程序里處理表情字符的開發(fā)者。前面的編碼原理部分稍硬但我會盡量用生活化的類比講后半部分的實操配置可以直接抄。我見過太多人把“隱藏代碼”理解成某種秘鑰以為輸入一串字符就能解鎖系統(tǒng)里藏著的表情包。實際情況要樸素得多所謂的“隱藏”指的是這些字符在鍵盤面板上找不到入口但在文本層面是合法存在的你可以復(fù)制、可以粘貼、可以存成快捷短語。一旦理解了這一點后面所有的玩法都是水到渠成的事。1.1 一條表情消息的“身份證”碼位、碼點與字節(jié)任何一個表情在 Unicode 標準里都有一個唯一的編號寫作 UXXXX。比如那個最常見的笑臉編號是 U1F600一個心形是 U2764。這個編號叫碼位它只是一個抽象的數(shù)字跟“長什么樣”沒有必然關(guān)系——具體畫成什么樣由字體文件決定。這就像身份證號和人臉的關(guān)系號碼唯一但同一個人在不同相機里拍出來氣質(zhì)可能完全不同。真正傳輸?shù)臅r候這個數(shù)字要變成字節(jié)。UTF-8 編碼下U1F600 會被編成 4 個字節(jié)F0 9F 98 80。UTF-16 下則是兩個 16 位單元也就是所謂的代理對Surrogate PairD83D DE00。這里就是第一個大坑——很多老的接口、老的數(shù)據(jù)庫字段、老的文本處理函數(shù)是按“一個 16 位單元等于一個字符”來設(shè)計的遇到代理對就會把一個表情切成兩半于是你看到的就是兩個問號或者一個亂碼方塊。我做社群文案的時候踩過一次把一段帶表情的文案粘進某個統(tǒng)計表格導(dǎo)出的 CSV 里表情全變成了問號。排查了半天才發(fā)現(xiàn)那個工具按 UTF-16 單元截斷字符串正好把一個代理對從中間劈開了。后來我的做法是凡是涉及表情的文本處理一律先確認編碼是 UTF-8并且用能正確處理碼位而不是字節(jié)或 16 位單元的函數(shù)來切分。理解這一層之后你就明白為什么“復(fù)制粘貼”是表情傳播的主要方式復(fù)制的是碼位序列不是圖像。這也解釋了另一個現(xiàn)象——你把微信里的表情復(fù)制到記事本可能變成黑白或者方塊因為記事本用的字體不一定覆蓋這個碼位。1.2 為什么同一個表情在不同手機上長得不一樣字體覆蓋率是第一層原因。系統(tǒng)字體比如各家的系統(tǒng) emoji 字體會定期更新新加入的表情在老系統(tǒng)上就沒有對應(yīng)字形只能退化顯示成空白方塊或者“豆腐塊”。這就是為什么你發(fā)一個剛出的新表情用舊手機的朋友會回你一句“你發(fā)的啥我這邊是空白”。第二層原因是呈現(xiàn)方式的選擇。Unicode 里有一批字符是“雙重身份”的它既可以當(dāng)普通的文本符號比如一個黑白的心形、一個普通的星號也可以當(dāng)彩色表情。到底畫成哪種取決于后面跟不跟一個叫變體選擇符的隱形字符。這個機制是后面第三章的重點也是很多“我明明復(fù)制對了怎么變黑白了”的根源。第三層原因是平臺對組合序列的支持程度。有些表情是由好幾個碼位拼起來的比如“一家人”“女醫(yī)生”“彩虹旗”。支持完整的系統(tǒng)能拼出正確圖形支持一半的系統(tǒng)可能只顯示第一個組成部分于是“一家三口”變成了“一個人”。這三層原因決定了后面所有排查工作的順序先看碼位對不對再看有沒有丟變體選擇符最后才懷疑字體版本。1.3 誰適合看這篇三類人的不同用法第一類是純聊天用戶。你不需要記任何碼位只需要跳到第四章把常用的組合存成輸入法的自定義短語兩三個字母就能打出一長串。第四章給了三套方案按你的動手意愿選一套就行。第二類是內(nèi)容運營和文案崗。你需要的是“穩(wěn)定不翻車”也就是發(fā)出去之后在大面積設(shè)備上都能正常顯示。這類讀者重點看第五章那里有一張故障對照表覆蓋了顯示成方塊、被裁切、變成問號、字數(shù)超限這幾種情況照著查就行。第三類是開發(fā)者。你需要的是可編程的處理方式包括怎么識別一段文本里的表情、怎么安全地截斷、怎么構(gòu)造組合序列。第四章第三節(jié)有一段可以直接跑的腳本覆蓋了拆分碼位、檢測組合序列、批量替換這幾個常用動作。順便說一句網(wǎng)上流傳的很多“表情代碼大全”其實是把碼位表直接貼出來不解釋結(jié)構(gòu)也不說明版本。這類資料的問題在于你照著抄了一旦顯示不對就無從下手。這篇的寫法是先講結(jié)構(gòu)再給表目的是讓你具備自己查、自己驗的能力。2. SoftBank、Unified 與那套被收編的老編碼要講清楚對照表必須先講一段歷史。在 Unicode 正式收錄表情之前早期的移動端平臺各自在自己系統(tǒng)的私有區(qū)域里定義了一套圖形字符。私有使用區(qū)Private Use Area是 Unicode 里預(yù)留的一段范圍大致在 UE000 到 UF8FF 之間標準不規(guī)定這些碼位代表什么留給各家自己用。SoftBank 當(dāng)年的表情就放在這個區(qū)域里所以你在老資料里看到的 E04A、E04B 這類四位編號本質(zhì)上就是“私有約定”不是標準字符。2.1 私有使用區(qū)的歷史位置與它帶來的麻煩私有使用區(qū)的好處是自由壞處是不可移植。同一段字節(jié)在 A 家的設(shè)備上是太陽在 B 家的設(shè)備上可能是別的圖形甚至什么都不顯示。當(dāng)年的跨平臺聊天經(jīng)常出現(xiàn)“我發(fā)的是笑臉你收到的是問號”根子就在這兒。后來統(tǒng)一碼聯(lián)盟做了一件很重要的事把這些民間表情逐一收編進標準碼位。收編的過程中產(chǎn)生了一張映射表也就是常說的 emoji 對照表把各家私有碼位對應(yīng)到統(tǒng)一的碼位。SoftBank 那一列就是從這張表里來的。這里有個細節(jié)值得注意映射不是一對一的完美替換。有些私有碼位在標準里有多個候選有些標準表情在私有區(qū)里壓根沒有對應(yīng)物還有些映射在不同版本的映射文件里注釋不一致。所以你手里的對照表如果和別人的對不上先別懷疑自己抄錯了很可能是版本差異。判斷依據(jù)應(yīng)該是碼位本身加上名稱而不是憑圖形去猜。2.2 Unified 是怎么把這些表情收編的統(tǒng)一碼的做法是分塊收錄。表情主要集中在幾個區(qū)塊里U1F300 到 U1F5FF 是雜項符號和象形文字U1F600 到 U1F64F 是表情臉U1F680 到 U1F6FF 是交通和地圖符號U2600 到 U27BF 里則散落著一批從傳統(tǒng)符號“升級”成表情的字符比如太陽、云、雨傘、心形、星號。所謂“升級”就是我前面說的雙重身份。U2600 這個碼位在標準里的正式名稱是“帶射線的黑色太陽”它本來就是個文本符號后來為了兼容表情需求標準規(guī)定如果它后面跟著變體選擇符 UFE0F就應(yīng)當(dāng)以彩色表情形式呈現(xiàn)如果跟著 UFE0E或者什么都不跟就以文本形式呈現(xiàn)。這一條是理解“隱藏代碼”的關(guān)鍵。SoftBank 那批老碼位絕大多數(shù)被映射到了這些標準碼位上。所以你手上如果有一份 SoftBank 編號想在新設(shè)備上用正確做法是查表換成 Unified 碼位而不是原樣粘貼。原樣粘貼的私有區(qū)字符新設(shè)備上大概率什么都顯示不出來——因為那段范圍現(xiàn)在基本是空的。2.3 對照表怎么讀三列結(jié)構(gòu)最實用我建議你自己維護的對照表至少包含三列SoftBank 碼位、Unified 碼位、名稱。名稱這一列別省它是你排查時的錨點。圖形列可以加但優(yōu)先級最低因為圖形會隨字體版本變化。下面這張表給的是結(jié)構(gòu)演示列了幾個高頻的天氣類示例。注意不同版本的映射文件對同一碼位的注釋可能存在出入實際使用時請以你的字庫和映射文件為準并用實測顯示來驗證。SoftBank 碼位Unified 碼位名稱備注E04AU2600 UFE0F太陽需跟變體選擇符才顯示彩色E04BU2601 UFE0F云同上E04CU2614帶雨的傘標準碼位本身即表情E000 段分散映射大量圖形需逐條查表不可批量假設(shè)無對應(yīng)U1F600 起新表情臉私有區(qū)時代不存在讀這張表的時候要抓住一個邏輯左邊是歷史右邊是現(xiàn)在。你在網(wǎng)上看到一段“表情代碼”先判斷它是哪一列的東西。如果是 E 開頭的四位十六進制那基本是私有區(qū)老碼位需要轉(zhuǎn)換如果是 U1F 開頭那已經(jīng)是標準碼位直接用就行但要留意變體選擇符和組合序列。另外提醒一個實操經(jīng)驗不要試圖把整張對照表背下來。真正有用的是記住幾個高頻的剩下的靠查。我自己的做法是維護一個純文本文件左邊寫“關(guān)鍵詞”右邊寫“可以直接粘貼的字符”需要的時候搜關(guān)鍵詞。這比記碼位高效得多也更不容易出錯。3. 五類高頻“隱藏代碼”逐個拆解這一章是全文最硬的部分但也是最值錢的。把這五類搞清楚你看到任何一串“神秘代碼”都能自己判斷它是什么、為什么這么寫、會不會翻車。3.1 變體選擇符一個看不見的字符決定彩色還是黑白變體選擇符有兩個常用的UFE0F 表示“請用表情形式呈現(xiàn)”UFE0E 表示“請用文本形式呈現(xiàn)”。它們本身不占顯示寬度也不產(chǎn)生任何圖形純粹是給渲染器的指令。這就是最典型的“隱藏代碼”——你眼睛看不見它但它決定了你看到的是彩色小人還是黑白符號。舉幾個常見的例子。心形 U2764不帶變體選擇符時很多系統(tǒng)畫成細黑線的心加上 UFE0F 之后變成飽滿的紅色愛心。太陽 U2600、云 U2601、雨傘 U2614 都是同一套邏輯。還有那個非常常用的對勾 U2714、叉號 U274C也都是靠這個開關(guān)切換形態(tài)。這里的坑在于復(fù)制粘貼的過程中變體選擇符極易丟失。因為它不可見很多編輯器、很多輸入框在粘貼時會做“清理不可見字符”的處理順手就把它刪了。你會發(fā)現(xiàn)自己復(fù)制的時候明明是彩色的粘到別處就變成黑白線條了。這不是錯覺。排查方法很簡單把那段文本粘到一個能看到碼位的工具里很多編輯器有“顯示不可見字符”的選項數(shù)一下心形后面有沒有 FE0F。如果沒有手動補一個或者換一個不會被清理的路徑。再講一個更隱蔽的情況有些平臺會在存儲時把變體選擇符規(guī)范化掉。比如某次我做一批文案在本地編輯器里全部正常導(dǎo)進某個后臺之后全部變黑白。后來發(fā)現(xiàn)是那個后臺的輸入框?qū)ξ谋咀隽?NFC 規(guī)范化之外的額外處理。解決辦法是改用圖片或者干脆選一個本身就是表情碼位的字符比如 U1F49B 黃色心形繞開這個開關(guān)。3.2 零寬連接符家庭、職業(yè)、情侶表情的拼裝邏輯零寬連接符ZWJ的碼位是 U200D。它的作用是把前后兩個表情“焊”在一起告訴渲染器這兩個別分開畫請合成一個新圖形。最典型的應(yīng)用是家庭組合。一個人 ZWJ 另一個人渲染器一看這個結(jié)構(gòu)就畫成“兩個人手拉手”。再加一個 ZWJ 和一個小孩就變成“一家三口”。同樣的邏輯用在職業(yè)上一個人 ZWJ 一個工具符號就變成“某個職業(yè)的人”。用在情侶上兩個人 ZWJ 心形就變成“帶心的情侶”。這個機制的妙處在于組合爆炸基礎(chǔ)元素就那么幾十個通過 ZWJ 拼接能造出成千上萬種組合。這也是為什么每年新增的表情看起來很多實際上很多是新組合而不是新圖形。但它也是兼容性最差的一類。老系統(tǒng)不認識這個序列就會退化成“把每個組成部分單獨畫出來”于是“一家三口”變成“三個人并排”中間還多出一個空白ZWJ 本身不顯示但可能占位。所以在正式場合發(fā)這類組合表情一定要先在目標設(shè)備上預(yù)覽。我自己的經(jīng)驗是群里發(fā)著玩隨便用正式文案一律不用 ZWJ 組合。因為受眾設(shè)備太雜你沒法保證每個人都看到你想要的效果。順帶提一個常見的誤解。有人以為 ZWJ 是“刪除”或者“連接兩個詞”的其實它在排版里確實有“不斷行”的語義在表情里則是“合成”的語義。同一個字符在不同上下文里承擔(dān)不同職責(zé)這在 Unicode 里很常見不用糾結(jié)看上下文就行。3.3 膚色修飾符五個檔位的疊加順序有講究膚色修飾符一共五個碼位從 U1F3FB 到 U1F3FF分別對應(yīng)從淺到深的五個檔位。用法是緊跟在具備膚色屬性的人體表情之后。關(guān)鍵的規(guī)則是順序。修飾符必須跟在基礎(chǔ)表情之后不能放前面。比如“豎起大拇指”加“中等膚色”正確的寫法是大拇指在前、修飾符在后。放反了渲染器就認不出來會顯示成兩個獨立的東西一個大拇指加一個色塊。第二個規(guī)則是只能跟一個。跟兩個修飾符是非法的渲染器的行為不確定有的取第一個有的直接忽略全部。第三個規(guī)則是不是所有表情都支持。只有標準里標注了“emoji modifier base”的那批人體部位或人物表情才支持比如手勢、人物、部分職業(yè)。如果你給一個蘋果加膚色修飾符它是不會變色的。這條規(guī)則在實際使用中的價值在于它解釋了為什么“同一個手勢別人發(fā)的深色我發(fā)的淺色”。不是版本問題是修飾符不同。還有個小細節(jié)值得留意膚色修飾符在組合序列里的位置。比如“女醫(yī)生”加膚色標準寫法是“人物 ZWJ 職業(yè)符號 膚色修飾符”修飾符放在整個序列的最后。放在中間某些位置部分渲染器會認部分不會屬于“能跑但不穩(wěn)”的寫法不建議用。3.4 國旗與鍵帽區(qū)域指示符與合成序列國旗的實現(xiàn)方式很有意思。它沒有為每個國家單獨分配一個碼位那樣太浪費而是用了區(qū)域指示符號U1F1E6 到 U1F1FF 這 26 個碼位對應(yīng)字母 A 到 Z。兩個區(qū)域指示符連在一起就代表一個兩位的國家或地區(qū)代碼渲染器查表把它畫成對應(yīng)的旗幟。比如某個兩字母代碼寫成兩個對應(yīng)的區(qū)域指示符就出旗子。這個設(shè)計很優(yōu)雅但也有個副作用在不支持的國家或地區(qū)它會退化成兩個方框字母。你會看到兩個帶字母的方塊并排看起來像亂碼。這不是錯誤是降級顯示。鍵帽序列是另一類合成。它的結(jié)構(gòu)是一個基礎(chǔ)字符數(shù)字 0-9、井號、星號 變體選擇符 UFE0F 組合用鍵帽 U20E3。三個部分缺一不可。少了 U20E3就只是一個數(shù)字少了 FE0F某些系統(tǒng)上會顯示成文本樣式順序錯了整個序列就散了。這一類在聊天里的用途是編號。比如做群公告的步驟列表用鍵帽數(shù)字比用普通數(shù)字更醒目。但要注意鍵帽序列在某些輸入法里會被當(dāng)成普通字符處理粘貼后可能被拆開用之前先預(yù)覽一下。3.5 標簽序列那類幾乎沒人提的隱形標簽還有一類更冷門的東西標簽字符。碼位范圍大致在 UE0020 到 UE007F配合 U1F3F4黑旗使用可以拼出一些特殊的旗幟比如某些地區(qū)的旗幟就是“黑旗 一串標簽字符 終止符”組成的。這類序列的特點是支持度極低。很多系統(tǒng)壓根不認直接把整串顯示成黑旗加一串空白或亂碼。所以網(wǎng)上流傳的一些“隱藏旗幟代碼”你在自己手機上可能是正常的發(fā)給別人就變成一坨。我的建議很直接除非你有明確的驗證手段否則不要用標簽序列。它屬于“技術(shù)上存在實用價值有限”的那一類。真要表達用文字說清楚更穩(wěn)妥。4. 在聊天框里真正用起來三套可復(fù)制的實操方案原理講完了接下來是能直接抄的部分。我給三套方案按動手成本從低到高排你按自己的情況選。4.1 方案一自定義短語兩三個字母打出一長串這是性價比最高的一套。絕大多數(shù)輸入法都支持“自定義短語”或“文本替換”功能你可以設(shè)一條規(guī)則輸入某個短碼自動展開成一長串內(nèi)容。具體操作路徑各家輸入法略有不同但邏輯一致進設(shè)置找“詞庫”或“快捷輸入”或“自定義短語”新建一條左邊填觸發(fā)碼右邊填展開內(nèi)容。觸發(fā)碼建議用不會和正常輸入沖突的組合比如兩個不常用字母加一個數(shù)字。這里的關(guān)鍵技巧是觸發(fā)碼要有語義。比如你想存一個“點贊三連”的組合觸發(fā)碼可以用“dz3”存一個“分隔線”用“fgx”。有語義的短碼記起來毫不費力純隨機碼過兩周就忘了。第二個技巧是分組管理。我自己的短語庫分了四組常用表情組合、分隔線與邊框、常用文案模板、特殊符號。每組用不同的前綴比如表情組統(tǒng)一用“e”開頭符號組統(tǒng)一用“s”開頭。這樣即使短語條數(shù)上百也不會互相干擾。第三個技巧是存“結(jié)構(gòu)”而不是“成品”。什么意思如果你存了一整句話帶表情用的時候只能整句用如果你存的是“表情 空格 占位”用的時候還能改。我更傾向于存短小的片段讓組合在輸入時完成靈活度高得多。這套方案唯一的缺點是換設(shè)備要重新配。有些輸入法支持賬號同步能省點事不支持的話建議把你的短語庫導(dǎo)出成一個純文本文件存著換機時批量導(dǎo)回去。4.2 方案二建一個“速查表”收藏夾隨用隨取如果你不想配輸入法或者用的設(shè)備經(jīng)常換那就用最原始也最可靠的辦法建一個純文本速查表。具體做法在任意筆記工具里新建一條按用途分區(qū)每區(qū)下面一行一個條目左邊寫關(guān)鍵詞右邊寫可以直接復(fù)制的內(nèi)容。用的時候搜索關(guān)鍵詞復(fù)制粘貼。這個方案看起來笨但有兩個無可替代的優(yōu)勢。第一是跨設(shè)備零成本只要筆記能同步哪臺設(shè)備都能用。第二是可驗證你可以在同一條筆記里同時存“預(yù)期效果”的說明時間久了自己也不會忘。我的速查表里有一欄是專門記“坑”的。比如某條組合在某個平臺上會顯示成方塊我就在旁邊標注一下。這個習(xí)慣幫我省了很多重復(fù)排查的時間。4.3 方案三用腳本批量生成與清洗如果你要處理成百上千條或者需要把一批文案里的老碼位統(tǒng)一換成新碼位那就得上腳本了。下面這段 Python 覆蓋了三個最常用的動作拆分碼位、檢測組合序列、批量替換。import re import unicodedata # 1. 把一段文本拆成碼位方便肉眼檢查有沒有丟變體選擇符 def dump_codepoints(text): for ch in text: cp ord(ch) try: name unicodedata.name(ch) except ValueError: name 未命名 print(fU{cp:04X}\t{name}) # 2. 檢測是否包含組合序列ZWJ 或變體選擇符或膚色修飾符 def has_sequence(text): patterns { ZWJ: \u200d, VS16: \ufe0f, 膚色修飾符: [\U0001F3FB-\U0001F3FF], 鍵帽: \u20e3, 標簽字符: [\U000E0020-\U000E007F], } return {k: bool(re.search(v, text)) for k, v in patterns.items()} # 3. 把私有使用區(qū)的老碼位替換成標準碼位 # 映射關(guān)系請以你自己的映射文件為準這里只放兩條演示 SB_TO_UNIFIED { \ue04a: \u2600\ufe0f, # 太陽 \ue04b: \u2601\ufe0f, # 云 } def convert_softbank(text): for old, new in SB_TO_UNIFIED.items(): text text.replace(old, new) return text if __name__ __main__: sample 測試\ue04a文本\u2764\ufe0f組合 print(--- 碼位拆解 ---) dump_codepoints(sample) print(--- 序列檢測 ---) print(has_sequence(sample)) print(--- 替換后 ---) print(convert_softbank(sample))這段腳本的價值不在于它本身多復(fù)雜而在于它把“看不見的東西”變成了“看得見的東西”。你只要跑一次 dump_codepoints就能立刻知道一段文本里到底藏了什么、有沒有缺東西。這個動作我在排查顯示問題時做了不下幾十次。補充一句實操提醒處理文本時不要用按字節(jié)切片的函數(shù)。要按碼位或者按字形簇grapheme cluster切。Python 里按碼位遍歷是安全的但如果要按“用戶感知的一個字符”切就得用專門的庫因為一個表情可能由好幾個碼位組成直接從中間切開就廢了。5. 踩坑記錄顯示異常、被裁切、變成問號的排查順序這一章是我這些年攢下來的故障清單按發(fā)生頻率排序。遇到問題從上往下查基本能覆蓋九成情況。5.1 常見故障對照表現(xiàn)象最可能的原因排查動作解決方式顯示成空白方塊字體不覆蓋該碼位查碼位是否屬于較新版本換舊表情或改用文字變成黑白線條丟了變體選擇符 FE0F用腳本 dump 碼位手動補 FE0F變成兩個問號按 16 位單元截斷代理對被劈開檢查處理鏈路的編碼全程用 UTF-8按碼位切組合表情散開系統(tǒng)不支持 ZWJ 序列換一臺設(shè)備預(yù)覽改用單個表情粘貼后內(nèi)容變少編輯器清理了不可見字符換粘貼路徑用純文本編輯器中轉(zhuǎn)發(fā)送后超出字數(shù)限制組合序列占多個碼位數(shù)一下實際碼位數(shù)精簡或拆成兩條旗幟變成兩個字母塊區(qū)域指示符降級顯示確認目標平臺是否支持改用文字表述這張表最有用的是第三行。很多人以為“問號”是表情不存在其實往往是編碼被截斷了。區(qū)分方法很簡單如果是一個方塊多半是字體沒有如果是兩個問號或者一個問號加半個亂碼多半是字節(jié)層面被切了。5.2 排查順序從碼位到字體再到平臺我習(xí)慣按這個順序走能少走很多彎路。第一步確認碼位本身是對的。用前面那段腳本 dump 一下看看是不是你想要的碼位有沒有多出或少掉不可見字符。這一步能解決大部分問題因為很多故障源于復(fù)制過程中丟了東西。第二步確認呈現(xiàn)方式。如果是那種“雙重身份”的字符檢查變體選擇符在不在。這一步專門解決“為什么是黑白的”。第三步確認組合結(jié)構(gòu)。如果是多碼位組合檢查順序?qū)Σ粚?、有沒有非法疊加。這一步解決“為什么散開了”。第四步確認字體版本。把同一段文本發(fā)到另一臺設(shè)備上看如果那邊正常說明是當(dāng)前設(shè)備的字體問題不是內(nèi)容問題。第五步確認平臺處理。如果所有設(shè)備都不對那就得懷疑中間經(jīng)過了某個會改文本的環(huán)節(jié)比如輸入框、后臺、導(dǎo)出工具。這時候換一條路徑比如先存成文件再發(fā)能快速定位。這個順序的邏輯是從內(nèi)容到環(huán)境因為內(nèi)容問題占的比例最大而且改起來最便宜。5.3 幾個容易被忽略的細節(jié)第一個細節(jié)是字數(shù)統(tǒng)計。很多平臺按碼位計數(shù)一個組合表情可能占五六個碼位。你以為發(fā)了一個表情實際上占了六個字的位置。做長文案的時候要留余量不然會被截斷。我遇到過一條文案被硬生生截掉半個組合表情最后顯示成一堆碎片非常難看。第二個細節(jié)是昵稱和簽名的限制。有些位置對字符類型有限制某些組合序列會被過濾掉或者被規(guī)范化成別的形式。設(shè)置完之后一定要退出來看一眼實際效果別只信編輯框里的預(yù)覽。第三個細節(jié)是復(fù)制到不同應(yīng)用時的差異。同一段文本從聊天框復(fù)制到備忘錄、從備忘錄復(fù)制到表格每經(jīng)過一個環(huán)節(jié)都可能被處理一次。我的經(jīng)驗是要跨應(yīng)用搬運帶表情的內(nèi)容先用純文本編輯器做中轉(zhuǎn)。純文本編輯器最“老實”不會自作主張改你的字符。第四個細節(jié)是輸入法的聯(lián)想干擾。你打某個短碼輸入法可能彈出聯(lián)想的候選詞把你要的短語頂?shù)?。解決方法是把觸發(fā)碼設(shè)得更“不像詞”比如加個數(shù)字或者用雙寫字母。6. 組合玩法與邊界能做的不等于該做的講完原理和排查最后聊聊怎么用得好看、以及什么場合該收手。6.1 分隔線與邊框的構(gòu)造分隔線是最實用的組合玩法。做法很簡單選一個重復(fù)起來好看的符號復(fù)制十到二十個中間不加空格。常用的有橫線類、波浪類、方塊類。要點是選等寬或接近等寬的符號不然排出來會歪。邊框稍微麻煩一點因為要考慮對齊。我的做法是用三行第一行是左上角、若干橫線、右上角第二行是豎線、空格、豎線第三行是左下角、橫線、右下角。但不同設(shè)備上字符寬度不一致很容易歪所以要留出調(diào)整余地寧可少用幾個字符。這里有個反直覺的經(jīng)驗不要追求絕對對齊。在聊天場景里稍微不齊沒人會在意但如果你為了對齊用了大量全角半角混排在某些設(shè)備上會崩得很慘。我的原則是“看起來大致整齊就行”把精力放在內(nèi)容上。6.2 組合表情的“語法”順序、數(shù)量與嵌套組合表情不是隨便拼的。有三條經(jīng)驗規(guī)則。第一順序有意義。ZWJ 連接的前后是有方向的某些組合反過來就是不成立的。膚色修飾符必須在最后。變體選擇符必須緊跟基礎(chǔ)字符。第二數(shù)量有上限。一個組合序列里塞太多 ZWJ渲染器可能放棄合成直接攤開顯示。我試過把四個元素連在一起在部分設(shè)備上就散架了。一般兩到三個元素是安全區(qū)。第三不要嵌套。把兩個已經(jīng)合成好的組合再用 ZWJ 連起來屬于未定義行為各家表現(xiàn)不一致。這種寫法看著聰明實際不可靠。6.3 什么場合該收手最后說點“不技術(shù)”但更重要的事。第一正式溝通少用組合表情。給客戶、給上級、給不熟的人發(fā)消息組合表情的兼容風(fēng)險不值當(dāng)。一個簡單的表情足夠表達語氣復(fù)雜組合留給熟人場景。第二不要用表情替代標點。有些人喜歡用表情當(dāng)逗號句號一句話里塞十幾個。在群聊里沒問題但在需要存檔、檢索、轉(zhuǎn)發(fā)的場合會大大降低可讀性。第三注意場合的文化差異。同一類圖形在不同語境里的含義可能完全不同跨群、跨年齡層發(fā)送之前想一想別把調(diào)侃發(fā)給不該調(diào)侃的人。我自己在實際操作中的體會是表情的價值在于“降低表達成本”而不是“炫技”。真正好用的不是那些復(fù)雜到?jīng)]人見過的組合而是那幾個你隨手就能打出來、對方一眼就能看懂的東西。把常用的二十個存成短語比背下來兩百個碼位有用得多。這套東西學(xué)到這里就夠了剩下的靠日常用。