1. 程式人生 > 程式設計 >不要小看小小的 emoji 表情

不要小看小小的 emoji 表情

前言

好久沒更新了,最近事比較多,或許下個月就會恢復到正常的發文頻次。

這篇文章得從一個 emoji 表情開始,我之前開源的一個 IM 專案中有朋友提到希望可以支援 emoji 表情傳輸。

github.com/crossoverJi…

正好那段時間有空,加上這功能看著也比較簡單準備把它實現了。

但在真正實現時卻發現沒那麼簡單。

我首先嚐試將一個 emoji 表情存入資料庫看看:

果不其然的出錯了,導致這個異常的原因是目前資料庫所支援的編碼中並不能存放 emoji,那 emoji 表情到底是個什麼東西呢。

本質上來說計算機所儲存的資訊都是二進位制 01emoji 也不例外,只要儲存和讀取(編解碼)的方式一致那就可以準確的展示這個資訊。

更多編解碼的內容後文再介紹,這裡先想想如何快速解決問題。

儲存 emoji

雖說想要在 MySQL 中儲存 emoji 的方式也有好幾種,比如可以升級儲存字符集到可以存放 emoji ,但這種需要 MySQL 的版本支援。

所以更保險的方式還是在應用層解決,比如我們是否可以將 emoji 當做字串儲存,只是顯示的時候要格式化為一個 emoji 表情,這樣對於所有的資料庫版本都可相容。

於是我們這裡的需求是一個 emoji 表情轉換為字串,同時還得將這個字串轉換為 emoji。

為此我在 GitHub 上找到了一個庫,它可以方便的將一個 emoji 轉換為字串的別名,同時也支援將這個別名轉換為 emoji

github.com/vdurmont/em…

    @Test
    public void emoji() throws Exception{
        String str = "An :grinning:awesome :smiley:string ?with a few :wink:emojis!";
        String result = EmojiParser.parseToUnicode(str);
        System.out.println(result);

        result = EmojiParser.parseToAliases(str);
        System.out.println(result);

    }複製程式碼

所以基於這個基礎庫最終實現了表情功能。

其實它本質上是自己維護了一個 emoji 的別名及它的 Unicode 編碼(本質上是 UTF-16)的對映關係,再每次格式化資料的時候都會從這個表中進行翻譯。

編碼知識回顧

自此需求是完成了,但還有幾個問題待解決。

  • Java 中是如何儲存 emoji 的?
  • emoji 是如何進行編碼的?

ASCII

在談 emoji 之前非常有必要了解下計算機編碼鼻祖的 ASCII 碼。

大家現在都知道在計算機內部儲存資料本質上都是二進位制的 0/1,對於一個位元組來說有 8 位;每一位可以表示兩種狀態,也就是 0 或 1,這樣排列組合下來,一個位元組就可以表示 256(2∧8) 種不同的狀態。

對於美國來說他們日常使用的英語只需要 26 個英文字母,再加上一些標點符號就足夠用計算機來進行資訊交流。

於是上個世紀 60年代定義了一套二進位制與英文字元的對映關係,可以表明 128 個不同的英文字元,也就是現在的 ASCII 碼。

這樣我們就可以使用一個位元組來表示現代英文,看起來非常不錯。

Unicode

隨著計算機的發展,逐漸在歐洲、亞洲地區流行;再利用這套 ASCII 碼進行資訊交流顯然是不行的,很多地區壓根就不使用英文,而且也遠超了 128 位字元(中文就更不用說了)。

雖說一個位元組在 ASCII 碼中只用了 128 位,但剩下(258-128)的依然不足用用於描述其他語言。

這時如果能有一種包含了世界上所有的文字的字符集,每一個地區的文字都在這個字符集中有唯一的二進位製表示,這樣便不會出現亂碼問題了。

Unicode 就是來做這個的,截止目前 Unicode 已經收錄了 10W+ 的字元,你所能使用的字元都包含進去了。

UTF-8

Unicode 雖說包含了幾乎所有的文字,但在我們日常使用好像很少看到他的身影,我們用的更多的還是 UTF-8 這樣的編碼規則。

這也有幾方面的原因,比如說除開英文,其他大部分的文字都需要用 2 個甚至更多的位元組來表示;如果統一都用 Unicode 來表示,那必然需要以佔用位元組最多的字元長度為標準。

比如漢字需要 2 個位元組來表示,而英文只需要一個位元組;這時就得規定 2 個位元組表示一個字元,不然漢字就沒法表示了。

但這樣也會帶來一個問題:用兩個位元組表示英文會使得第一個位元組完全是浪費的,如果一段資訊全是英文那對記憶體的浪費是巨大的。

這時大家應該都能想到,我們需要一個可變的長度的字元編碼規則,當是英文時我們就用一個位元組表示,甚至可以完全相容 ASCII 碼。

UTF-8 便是實現這個需求的,它利用兩種規則可以表示一個位元組以及多位元組的字元。

大致規則如下:

  • 當第一個位元組的第一位為 0 時便表示為單位元組字元,此時和 ASCII 碼一致,完全相容。
  • 當第一個位元組為 1 時,有幾個 1 便代表是幾個位元組 Unicode 字元。

這樣便可根據字元的長度最大程度的節省儲存空間。

當然還有其他的編碼規則,比如 UTF-16UTF-32,平時用的不多,但本質上都和 UTF-8 一樣,都是 Unicode 的不同實現,也是用於表示世界上大部分文字的字符集。

Java 中的 emoji

現在來回到本次的主題,emoji

剛才說到 Unicode 包含了世界上大部分的字元,emoji 自然也不例外。

apps.timwhitlock.info/emoji/table…

這個表格中包含了所有的 emoji 以及它所對應的 Unicode 編碼,同時也有對應的 UTF-8 編碼的實現。

從圖中也可以看出 emoji 表情用 UTF-8 表示時會佔用 4 個位元組,那在 Java 中它會是怎麼儲存的呢?

很簡單,debug 一下就知道了。

Java 中也是通過 char 來儲存 emoji 的,char 作為基本資料型別會佔用 2 個位元組;從剛才的圖中可以看出,emoji 使用 UTF-8 會佔用四個位元組,這樣很明顯 char 是沒法儲存的,所以在這裡其實是使用 UTF-16 編碼進行儲存。

基於這個原理,我們也可以自己實現將一個 emoji 表情轉換為字串,同時也可通過字串轉換為 emoji

總結

從這次研究 emoji 可以看出,任何一門基礎知識都是應用的根基,在計算機行業尤為突出,希望大家看完這篇能回憶起大學課堂被老師支配的恐懼?。

隨便提一下,相關原始碼可在這裡檢視:

github.com/crossoverJi…

你的點贊與分享是對我最大的支援