原創felix021felix021_3月29日_javascript
本文一共 3023 字,閱讀時間我不太會算。
在喬納森·斯威夫特的著名諷刺小說《格列夫遊記》中,小人國內部分裂成Big-endian和Little-endian兩派,區別在於一派要求從雞蛋的大頭把雞蛋打破,另外一派要求從雞蛋的小頭把雞蛋打破。html
而後忘了這個故事,我們開始吧。java
Charles同窗這周踩了個坑,數據插入MySQL時報錯:mysql
1366 Incorrect string value: '\xF0\x90\x8D\x83...' for column 'content' at row 1
按慣例搜一下,聽說是由於mysql用的 utf8 不支持 emoji,須要修改配置文件,將字符集改爲 utf8mb4:程序員
[mysqld]
character-set-server = utf8mb4
stackoverflow.com/questions/10957238
可是 Charles 同窗已經給 MySQL 加上了這個配置,仍然報錯。面試
實際上,MySQL 還有另一個配置,用於指定客戶端和服務器之間鏈接時使用的字符集:sql
[mysqld] character-set-client-handshake = utf8mb4
固然,也能夠在MySQL Client中指定,具體須要參考client的文檔,或者簡單粗暴地在鏈接成功之後執行(但不推薦):vim
SET NAMES utf8mb4;
那麼,什麼是utf8mb4?和utf8有啥區別呢?windows
根據MySQL的manual:後端
The utfmb4 character set has these characteristics:
- Supports BMP and supplementary characters.
- Requires a maximum of four bytes per multibyte character.
(文檔中utf8mb4打錯了,我是原樣複製的)
翻譯過來就是,utf8mb4 支持BMP(Unicode Basic Multilingual Plane)和補充字符,每一個字符最多 4 字節(這裏 「mb4」 大概就是 multi byte 4 的簡寫了)。
冷知識:Unicode編碼一共有 17 個 "Plane"(0~16),其中 Plane 0 就是BMP,包含絕大多數經常使用字符,好比希臘、希伯來、阿拉伯、CJK(Chinese-Japanese-Korean)字符等。Plane 1~16 被稱爲 "supplementary planes",包含不經常使用的其餘字符,例如 emoji 和某些特殊的CJK字符。因此目前 Unicode 字符的最大編碼爲 0x10FFFF。
至於 utf8,MySQL文檔裏也有說明:
utf8 is an alias for the utf8mb3 character set.Note
The utf8mb3 character set is deprecated and will be removed in a future MySQL release. Please use utf8mb4 instead
簡單說就是掛羊頭賣狗肉了,看到的是 utf8,實際用的是 utf8mb3 。
utf8mb3 的文檔就不貼了(懶),和 ut8mb4 的區別就在於最多隻支持3個字節,所以不支持Unicode的補充字符集。
也就是說,MySQL裏的utf8,其實是一個閹割版的utf8。
MySQL 從 5.5.3 纔開始支持完整版的 utf8(utf8mb4),而且後續計劃移除 utf8mb3,utf8 將來在mysql中也會變成 utf8mb4 的別名,因此之後默認都使用 utf8mb4 就對了。
話說回來,MySQL爲何會有這種奇怪的設定呢?
其實最初是從性能上考慮的,這個精簡版的 utf8 在運行的時候能夠更快一點點。
要知道 MySQL 已是一個 24 歲的老項目了,在1995年誕生時,Intel 才只推出了 Pentium Pro,對比如今的CPU,性能能夠說是很是差了。
冷知識:差到什麼程度呢?舉個例子,早期的 Windows Beta 版,桌面右下角時間是能夠顯示秒數的,但因爲當時硬件的性能問題,微軟在發佈 Windows 95 以前就移除了該功能,直到Windows 7(2009年)才容許經過修改註冊表開啓。
那麼真正的 utf8 長什麼樣呢?
在查文檔以前,不妨先動手建立一個文檔看一下
$ echo '0Aa你好' > utf-8.txt $ file utf-8.txt utf-8.txt: UTF-8 Unicode text $ xxd utf-8.txt #用16進制的方式查看 0000: 3041 61e4 bda0 e5a5 bd0a 0Aa.......
能夠看到,開頭"0Aa" 對應3個字節 0x30、0x4一、0x61(十進制4八、6五、97,大寫A < 小寫a 就是這麼來的)。
最後一個字符 0x0a 是 echo 默認輸出的換行。
冷知識:能夠加上 -n 參數讓 echo 不輸出換行符。換行符在不一樣OS下不一樣,在Linux/Unix下是 "\n" (0x0a),在Windows下是 "\r\n"(0x0d 0x0a),在早期Mac下是 "\r" (0x0d),從Mac OS 10.0(2001)開始也和Unix同樣用 "\n" 了。在C/Python等語言下,fopen/open默認使用「文本模式」打開文件,讀取時會統一轉換成 "\n",寫入時將"\n"轉換爲按os的默認值。還有一些其餘場景可能須要注意,例如http協議中header使用 "\r\n" 換行。
中間的 "e4bda0e5a5bd" 這 6 個字節對應的就是 「你好」 了,每一個字符 3 個字節。
那到底如何肯定一個 utf8 字符是幾個字節呢?
這裏貼一個 wikipedia 的表格:
簡單解釋一下:
前面展現了 ASCII 字符和中文,我們順便再看看 emoji 的 utf-8 編碼長什麼樣:
$ echo -n 😀 > 1.txt $ xxd 1.txt 0000: f09f 9880 ....
根據上面的表格,咱們能夠算出,這個GRIN FACE(露齒笑)的 Unicode 碼點是 0x1F600,在Unicode的Plain 1中,所以utf-8編碼須要4個字節。
上文提到了 Unicode 和 utf-8 這兩個名詞,可是不少同窗其實沒搞明白他倆的區別是啥。
通常咱們提到 Unicode 時指的是字符集(Character Set),其中包含了一系列字符,併爲每個字符指定了碼點(Code Point,即該字符的編號)。
Unicode標準裏包含了不少編碼規範,utf-8 是其中一種,指定了一個Unicode字符的碼點應該如何存儲,例如ASCII用一個字節,超過一個字節的根據須要的bit數量,分別存儲到多個字節中。
除了 utf-8 以外,Unicode還有多種不一樣的編碼規範,例如
然鵝 utf-8 幾乎統治了互聯網,超過93%的網頁是用UTF-8編碼的,以致於IETF要求全部網絡協議都須要標明內容的編碼,而且必須支持UTF-8。
至於緣由麼,還記得開頭的那個故事嗎?utf-8避免了上述編碼中的字節序(big endian、little endian)的問題。
固然這只是一個緣由,我認爲更重要的是,utf-8保持了對ascii的兼容,路徑依賴的強大慣性,會致使上述4種編碼在實際推廣中帶來很高的遷移成本(按理應該在這裏講講馬屁股寬度的故事,不過跑題太遠了)。
utf-8 在保持後向兼容的前提下,能支持全部Unicode字符,相比ucs4還能節省大量存儲空間、數據傳輸量,所以統治了互聯網,也就在情理之中了。
除了Unicode以外,還有不少其餘字符集,例如最經典的ASCII,因爲字符少,其編碼規範也至關簡單。
在中國,比較常見的字符集還有GB2312(1980年)、GBK(1993年)、GB18030(2000年),這些標準都規定了對應的編碼規範,因此這些名字既能夠表示字符集,也能夠表示編碼規範。
其中GB2312只包含 7445 個字符,其中漢字 6763 個(只包含經常使用漢字,不少生僻字都不支持),編碼規範也兼容ASCII。GBK(GB13000)兼容GB2312,添加了更多字符,GB18030是進一步的補充。
冷知識:咱們可使用 iconv 命令行工具來修改文件的字符編碼
$ iconv -f gb18030 -t utf-8 < gb18030.txt 0Aa你好
也能夠在vim中這麼幹
:set fileencoding=gb18030
此外,使用 windows 的同窗可能還見到過一個奇怪的代號 "cp936"(在上述iconv命令、vim中均可以使用),這是微軟對 GB2312 的實現,在 Win95 之後實際上也支持了大部分 GBK 字符。
~ 投遞連接 ~
後端開發(上海)
https://job.toutiao.com/s/sBAvKe
後端開發(北京)
https://job.toutiao.com/s/sBMyxk
廣告策略研發(上海)
https://job.toutiao.com/s/sBDMAK
全部職位
https://job.toutiao.com/s/sB9Jqk
微信掃碼