字符集一直在用,但是從未去了解過它。希望大家能一起討論討論。
字符集和比較規則簡介
字符集簡介
我們知道在計算機中只能儲存二進位制資料,那該怎麼儲存字串呢?當然是建立字元與二進位制資料的對映關係了,建立這個關係最起碼要搞清楚兩件事兒:
你要把哪些字元對映成二進位制資料?
也就是界定清楚字元範圍。
怎麼對映?
將一個字元對映成一個二進位制資料的過程也叫做
編碼
,將一個二進位制資料對映到一個字元的過程叫做解碼
。
人們抽象出一個字符集
的概念來描述某個字元範圍的編碼規則。比方說我們來自定義一個名稱為xiaohaizi
的字符集,它包含的字元範圍和編碼規則如下:
包含字元
'a'
、'b'
、'A'
、'B'
。編碼規則如下:
採用1個位元組編碼一個字元的形式,字元和位元組的對映關係如下:
'a' -> 00000001 (十六進位制:0x01) 'b' -> 00000010 (十六進位制:0x02) 'A' -> 00000011 (十六進位制:0x03) 'B' -> 00000100 (十六進位制:0x04)
有了xiaohaizi
字符集,我們就可以用二進位制形式表示一些字串了,下邊是一些字串用xiaohaizi
字符集編碼後的二進位制表示:
'bA' -> 0000001000000011 (十六進位制:0x0203)
'baB' -> 000000100000000100000100 (十六進位制:0x020104)
'cd' -> 無法表示,字符集xiaohaizi不包含字元'c'和'd'
比較規則簡介
在我們確定了xiaohaizi
字符集表示字元的範圍以及編碼規則後,怎麼比較兩個字元的大小呢?最容易想到的就是直接比較這兩個字元對應的二進位制編碼的大小,比方說字元'a'
的編碼為0x01
,字元'b'
的編碼為0x02
,所以'a'
小於'b'
,這種簡單的比較規則也可以被稱為二進位制比較規則,英文名為binary collation
。
二進位制比較規則是簡單,但有時候並不符合現實需求,比如在很多場合對於英文字元我們都是不區分大小寫的,也就是說'a'
和'A'
是相等的,在這種場合下就不能簡單粗暴的使用二進位制比較規則了,這時候我們可以這樣指定比較規則:
- 將兩個大小寫不同的字元全都轉為大寫或者小寫。
- 再比較這兩個字元對應的二進位制資料。
這是一種稍微複雜一點點的比較規則,但是實際生活中的字元不止英文字元一種,比如我們的漢字有幾萬之多,對於某一種字符集來說,比較兩個字元大小的規則可以制定出很多種,也就是說同一種字符集可以有多種比較規則,我們稍後就要介紹各種現實生活中用的字符集以及它們的一些比較規則。
一些重要的字符集
不幸的是,這個世界太大了,不同的人制定出了好多種字符集
,它們表示的字元範圍和用到的編碼規則可能都不一樣。我們看一下一些常用字符集的情況:
ASCII
字符集共收錄128個字元,包括空格、標點符號、數字、大小寫字母和一些不可見字元。由於總共才128個字元,所以可以使用1個位元組來進行編碼,我們看一些字元的編碼方式:
'L' -> 01001100(十六進位制:0x4C,十進位制:76) 'M' -> 01001101(十六進位制:0x4D,十進位制:77)
ISO 8859-1
字符集共收錄256個字元,是在
ASCII
字符集的基礎上又擴充了128個西歐常用字元(包括德法兩國的字母),也可以使用1個位元組來進行編碼。這個字符集也有一個別名latin1
。GB2312
字符集收錄了漢字以及拉丁字母、希臘字母、日文平假名及片假名字母、俄語西裡爾字母。其中收錄漢字6763個,其他文字元號682個。同時這種字符集又相容
ASCII
字符集,所以在編碼方式上顯得有些奇怪:- 如果該字元在
ASCII
字符集中,則採用1位元組編碼。 - 否則採用2位元組編碼。
這種表示一個字元需要的位元組數可能不同的編碼方式稱為
變長編碼方式
。比方說字串'愛u'
,其中'愛'
需要用2個位元組進行編碼,編碼後的十六進位制表示為0xB0AE
,'u'
需要用1個位元組進行編碼,編碼後的十六進位制表示為0x75
,所以拼合起來就是0xB0AE75
。小貼士: 我們怎麼區分某個位元組代表一個單獨的字元還是代表某個字元的一部分呢?別忘了
ASCII
字符集只收錄128個字元,使用0~127就可以表示全部字元,所以如果某個位元組是在0~127之內的,就意味著一個位元組代表一個單獨的字元,否則就是兩個位元組代表一個單獨的字元。- 如果該字元在
GBK
字符集GBK
字符集只是在收錄字元範圍上對GB2312
字符集作了擴充,編碼方式上相容GB2312
。utf8
字符集收錄地球上能想到的所有字元,而且還在不斷擴充。這種字符集相容
ASCII
字符集,採用變長編碼方式,編碼一個字元需要使用1~4個位元組,比方說這樣:'L' -> 01001100(十六進位制:0x4C) '啊' -> 111001011001010110001010(十六進位制:0xE5958A)
小貼士: 其實準確的說,utf8只是Unicode字符集的一種編碼方案,Unicode字符集可以採用utf8、utf16、utf32這幾種編碼方案,utf8使用1~4個位元組編碼一個字元,utf16使用2個或4個位元組編碼一個字元,utf32使用4個位元組編碼一個字元。更詳細的Unicode和其編碼方案的知識不是本書的重點,大家上網查查哈~ MySQL中並不區分字符集和編碼方案的概念,所以後邊嘮叨的時候把utf8、utf16、utf32都當作一種字符集對待。
對於同一個字元,不同字符集也可能有不同的編碼方式。比如對於漢字'我'
來說,ASCII
字符集中根本沒有收錄這個字元,utf8
和gb2312
字符集對漢字我
的編碼方式如下:
utf8編碼:111001101000100010010001 (3個位元組,十六進位制表示是:0xE68891)
gb2312編碼:1011000010101110 (2個位元組,十六進位制表示是:0xB0AE)
MySQL中的utf8和utf8mb4
我們上邊說utf8
字符集表示一個字元需要使用1~4個位元組,但是我們常用的一些字元使用1~3個位元組就可以表示了。而在MySQL
中字符集表示一個字元所用最大位元組長度在某些方面會影響系統的儲存和效能,所以設計MySQL
的大叔偷偷的定義了兩個概念:
utf8mb3
:閹割過的utf8
字符集,只使用1~3個位元組表示字元。utf8mb4
:正宗的utf8
字符集,使用1~4個位元組表示字元。
有一點需要大家十分的注意,在MySQL
中utf8
是utf8mb3
的別名,所以之後在MySQL
中提到utf8
就意味著使用1~3個位元組來表示一個字元,如果大家有使用4位元組編碼一個字元的情況,比如儲存一些emoji表情啥的,那請使用utf8mb4
。
本作品採用《CC 協議》,轉載必須註明作者和本文連結