自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

十分鐘搞清字符集和字符編碼

系統(tǒng) Linux
在介紹字符集之前,我們先了解下為什么要有字符集。我們?cè)谟?jì)算機(jī)屏幕上看到的是實(shí)體化的文字,而在計(jì)算機(jī)存儲(chǔ)介質(zhì)中存放的實(shí)際是二進(jìn)制的比特流。那 么在這兩者之間的轉(zhuǎn)換規(guī)則就需要一個(gè)統(tǒng)一的標(biāo)準(zhǔn),否則把我們的U盤(pán)插到老板的電腦上,文檔就亂碼了;小伙伴QQ上傳過(guò)來(lái)的文件,在我們本地打開(kāi)又亂碼了。

[[129199]]

什么是字符集

在介紹字符集之前,我們先了解下為什么要有字符集。我們?cè)谟?jì)算機(jī)屏幕上看到的是實(shí)體化的文字,而在計(jì)算機(jī)存儲(chǔ)介質(zhì)中存放的實(shí)際是二進(jìn)制的比特流。那 么在這兩者之間的轉(zhuǎn)換規(guī)則就需要一個(gè)統(tǒng)一的標(biāo)準(zhǔn),否則把我們的U盤(pán)插到老板的電腦上,文檔就亂碼了;小伙伴QQ上傳過(guò)來(lái)的文件,在我們本地打開(kāi)又亂碼了。 于是為了實(shí)現(xiàn)轉(zhuǎn)換標(biāo)準(zhǔn),各種字符集標(biāo)準(zhǔn)就出現(xiàn)了。簡(jiǎn)單的說(shuō)字符集就規(guī)定了某個(gè)文字對(duì)應(yīng)的二進(jìn)制數(shù)字存放方式(編碼)和某串二進(jìn)制數(shù)值代表了哪個(gè)文字(解 碼)的轉(zhuǎn)換關(guān)系。

那么為什么會(huì)有那么多字符集標(biāo)準(zhǔn)呢?這個(gè)問(wèn)題實(shí)際非常容易回答。問(wèn)問(wèn)自己為什么我們的插頭拿到英國(guó)就不能用了呢?為什么顯示器同時(shí)有 DVI,VGA,HDMI,DP這么多接口呢?很多規(guī)范和標(biāo)準(zhǔn)在最初制定時(shí)并不會(huì)意識(shí)到這將會(huì)是以后全球普適的準(zhǔn)則,或者處于組織本身利益就想從本質(zhì)上區(qū) 別于現(xiàn)有標(biāo)準(zhǔn)。于是,就產(chǎn)生了那么多具有相同效果但又不相互兼容的標(biāo)準(zhǔn)了。

說(shuō)了那么多我們來(lái)看一個(gè)實(shí)際例子,下面就是屌這個(gè)字在各種編碼下的十六進(jìn)制和二進(jìn)制編碼結(jié)果,怎么樣有沒(méi)有一種很屌的感覺(jué)?

字符集 16進(jìn)制編碼 對(duì)應(yīng)的二進(jìn)制數(shù)據(jù)
UTF-8 0xE5B18C 1110 0101 1011 0001 1000 1100
UTF-16 0x5C4C 1011 1000 1001 1000
GBK 0x8CC5 1000 1100 1100 0101

什么是字符編碼

字符集只是一個(gè)規(guī)則集合的名字,對(duì)應(yīng)到真實(shí)生活中,字符集就是對(duì)某種語(yǔ)言的稱呼。例如:英語(yǔ),漢語(yǔ),日語(yǔ)。對(duì)于一個(gè)字符集來(lái)說(shuō)要正確編碼轉(zhuǎn)碼一個(gè)字 符需要三個(gè)關(guān)鍵元素:字庫(kù)表(character repertoire)、編碼字符集(coded character set)、字符編碼(character encoding form)。其中字庫(kù)表是一個(gè)相當(dāng)于所有可讀或者可顯示字符的數(shù)據(jù)庫(kù),字庫(kù)表決定了整個(gè)字符集能夠展現(xiàn)表示的所有字符的范圍。編碼字符集,即用一個(gè)編碼值 code point來(lái)表示一個(gè)字符在字庫(kù)中的位置。字符編碼,將編碼字符集和實(shí)際存儲(chǔ)數(shù)值之間的轉(zhuǎn)換關(guān)系。一般來(lái)說(shuō)都會(huì)直接將code point的值作為編碼后的值直接存儲(chǔ)。例如在ASCII中A在表中排第65位,而編碼后A的數(shù)值是0100 0001也即十進(jìn)制的65的二進(jìn)制轉(zhuǎn)換結(jié)果。

看到這里,可能很多讀者都會(huì)有和我當(dāng)初一樣的疑問(wèn):字庫(kù)表和編碼字符集看來(lái)是必不可少的,那既然字庫(kù)表中的每一個(gè)字符都有一個(gè)自己的序號(hào),直接把序號(hào)作為存儲(chǔ)內(nèi)容就好了。為什么還要多此一舉通過(guò)字符編碼把序號(hào)轉(zhuǎn)換成另外一種存儲(chǔ)格式呢?

其實(shí)原因也比較容易理解:統(tǒng)一字庫(kù)表的目的是為了能夠涵蓋世界上所有的字符,但實(shí)際使用過(guò)程中會(huì)發(fā)現(xiàn)真正用的上的字符相對(duì)整個(gè)字庫(kù)表來(lái)說(shuō)比例非常 低。例如中文地區(qū)的程序幾乎不會(huì)需要日語(yǔ)字符,而一些英語(yǔ)國(guó)家甚至簡(jiǎn)單的ASCII字庫(kù)表就能滿足基本需求。而如果把每個(gè)字符都用字庫(kù)表中的序號(hào)來(lái)存儲(chǔ)的 話,每個(gè)字符就需要3個(gè)字節(jié)(這里以Unicode字庫(kù)為例),這樣對(duì)于原本用僅占一個(gè)字符的ASCII編碼的英語(yǔ)地區(qū)國(guó)家顯然是一個(gè)額外成本(存儲(chǔ)體積 是原來(lái)的三倍)。算的直接一些,同樣一塊硬盤(pán),用ASCII可以存1500篇文章,而用3字節(jié)Unicode序號(hào)存儲(chǔ)只能存500篇。于是就出現(xiàn)了 UTF-8這樣的變長(zhǎng)編碼。在UTF-8編碼中原本只需要一個(gè)字節(jié)的ASCII字符,仍然只占一個(gè)字節(jié)。而像中文及日語(yǔ)這樣的復(fù)雜字符就需要2個(gè)到3個(gè)字 節(jié)來(lái)存儲(chǔ)。

UTF-8和Unicode的關(guān)系

看完上面兩個(gè)概念解釋,那么解釋UTF-8和Unicode的關(guān)系就比較簡(jiǎn)單了。Unicode就是上文中提到的編碼字符集,而UTF-8就是字符 編碼,即Unicode規(guī)則字庫(kù)的一種實(shí)現(xiàn)形式。隨著互聯(lián)網(wǎng)的發(fā)展,對(duì)同一字庫(kù)集的要求越來(lái)越迫切,Unicode標(biāo)準(zhǔn)也就自然而然的出現(xiàn)。它幾乎涵蓋了 各個(gè)國(guó)家語(yǔ)言可能出現(xiàn)的符號(hào)和文字,并將為他們編號(hào)。詳見(jiàn):Unicode on Wikipedia。

Unicode的編號(hào)從0000開(kāi)始一直到10FFFF共分為16個(gè)Plane,每個(gè)Plane中有65536個(gè)字符。而UTF-8則只實(shí)現(xiàn)了第一 個(gè)Plane,可見(jiàn)UTF-8雖然是一個(gè)當(dāng)今接受度最廣的字符集編碼,但是它并沒(méi)有涵蓋整個(gè)Unicode的字庫(kù),這也造成了它在某些場(chǎng)景下對(duì)于特殊字符 的處理困難(下文會(huì)有提到)。

UTF-8編碼簡(jiǎn)介

為了更好的理解后面的實(shí)際應(yīng)用,我們這里簡(jiǎn)單的介紹下UTF-8的編碼實(shí)現(xiàn)方法。即UTF-8的物理存儲(chǔ)和Unicode序號(hào)的轉(zhuǎn)換關(guān)系。

UTF-8編碼為變長(zhǎng)編碼。最小編碼單位(code unit)為一個(gè)字節(jié)。一個(gè)字節(jié)的前1-3個(gè)bit為描述性部分,后面為實(shí)際序號(hào)部分。

如果一個(gè)字節(jié)的第一位為0,那么代表當(dāng)前字符為單字節(jié)字符,占用一個(gè)字節(jié)的空間。0之后的所有部分(7個(gè)bit)代表在Unicode中的序號(hào)。

如果一個(gè)字節(jié)以110開(kāi)頭,那么代表當(dāng)前字符為雙字節(jié)字符,占用2個(gè)字節(jié)的空間。110之后的所有部分(7個(gè)bit)代表在Unicode中的序號(hào)。且第二個(gè)字節(jié)以10開(kāi)頭

如果一個(gè)字節(jié)以1110開(kāi)頭,那么代表當(dāng)前字符為三字節(jié)字符,占用2個(gè)字節(jié)的空間。110之后的所有部分(7個(gè)bit)代表在Unicode中的序號(hào)。且第二、第三個(gè)字節(jié)以10開(kāi)頭

如果一個(gè)字節(jié)以10開(kāi)頭,那么代表當(dāng)前字節(jié)為多字節(jié)字符的第二個(gè)字節(jié)。10之后的所有部分(6個(gè)bit)代表在Unicode中的序號(hào)。

具體每個(gè)字節(jié)的特征可見(jiàn)下表,其中x代表序號(hào)部分,把各個(gè)字節(jié)中的所有x部分拼接在一起就組成了在Unicode字庫(kù)中的序號(hào)

Byte 1 Byte 2 Byte3
0xxx xxxx    
110x xxxx 10xx xxxx  
1110 xxxx 10xx xxxx 10xx xxxx

我們分別看三個(gè)從一個(gè)字節(jié)到三個(gè)字節(jié)的UTF-8編碼例子:

實(shí)際字符 在Unicode字庫(kù)序號(hào)的十六進(jìn)制 在Unicode字庫(kù)序號(hào)的二進(jìn)制 UTF-8編碼后的二進(jìn)制 UTF-8編碼后的十六進(jìn)制
$ 0024 010 0100 0010 0100 24
¢ 00A2 000 1010 0010 1100 0010 1010 0010 C2 A2
20AC 0010 0000 1010 1100 1110 0010 1000 0010 1010 1100 E2 82 AC

細(xì)心的讀者不難從以上的簡(jiǎn)單介紹中得出以下規(guī)律:

3個(gè)字節(jié)的UTF-8十六進(jìn)制編碼一定是以E開(kāi)頭的

2個(gè)字節(jié)的UTF-8十六進(jìn)制編碼一定是以C或D開(kāi)頭的

1個(gè)字節(jié)的UTF-8十六進(jìn)制編碼一定是以比8小的數(shù)字開(kāi)頭的

為什么會(huì)出現(xiàn)亂碼

先科普下亂碼的英文native說(shuō)法是mojibake。

簡(jiǎn)單的說(shuō)亂碼的出現(xiàn)是因?yàn)椋壕幋a和解碼時(shí)用了不同或者不兼容的字符集。對(duì)應(yīng)到真實(shí)生活中,就好比是一個(gè)英國(guó)人為了表示祝福在紙上寫(xiě)了bless(編 碼過(guò)程)。而一個(gè)法國(guó)人拿到了這張紙,由于在法語(yǔ)中bless表示受傷的意思,所以認(rèn)為他想表達(dá)的是受傷(解碼過(guò)程)。這個(gè)就是一個(gè)現(xiàn)實(shí)生活中的亂碼情 況。在計(jì)算機(jī)科學(xué)中一樣,一個(gè)用UTF-8編碼后的字符,用GBK去解碼。由于兩個(gè)字符集的字庫(kù)表不一樣,同一個(gè)漢字在兩個(gè)字符表的位置也不同,最終就會(huì) 出現(xiàn)亂碼。

我們來(lái)看一個(gè)例子:假設(shè)我們用UTF-8編碼存儲(chǔ)很屌兩個(gè)字,會(huì)有如下轉(zhuǎn)換:

字符 UTF-8編碼后的十六進(jìn)制
E5BE88
E5B18C

于是我們得到了E5BE88E5B18C這么一串?dāng)?shù)值。而顯示時(shí)我們用GBK解碼進(jìn)行展示,通過(guò)查表我們獲得以下信息:

兩個(gè)字節(jié)的十六進(jìn)制數(shù)值 GBK解碼后對(duì)應(yīng)的字符
E5BE
88E5
B18C

解碼后我們就得到了寰堝睂這么一個(gè)錯(cuò)誤的結(jié)果,更要命的是連字符個(gè)數(shù)都變了。

如何識(shí)別亂碼的本來(lái)想要表達(dá)的文字

要從亂碼字符中反解出原來(lái)的正確文字需要對(duì)各個(gè)字符集編碼規(guī)則有較為深刻的掌握。但是原理很簡(jiǎn)單,這里用最常見(jiàn)的UTF-8被錯(cuò)誤用GBK展示時(shí)的亂碼為例,來(lái)說(shuō)明具體反解和識(shí)別過(guò)程。

第1步 編碼

假設(shè)我們?cè)陧?yè)面上看到寰堝睂這樣的亂碼,而又得知我們的瀏覽器當(dāng)前使用GBK編碼。那么第一步我們就能先通過(guò)GBK把亂碼編碼成二進(jìn)制表達(dá)式。當(dāng)然查表編碼效率很低,我們也可以用以下SQL語(yǔ)句直接通過(guò)MySQL客戶端做編碼工作:

  1. mysql [localhost] {msandbox} > select hex(convert('寰堝睂' using gbk)); 
  2. +-------------------------------------+ 
  3. | hex(convert('寰堝睂' using gbk)) | 
  4. +-------------------------------------+ 
  5. | E5BE88E5B18C | 
  6. +-------------------------------------+ 
  7. 1 row in set (0.01 sec) 

第2步 識(shí)別

現(xiàn)在我們得到了解碼后的二進(jìn)制字符串E5BE88E5B18C。然后我們將它按字節(jié)拆開(kāi)。
Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6
E5 BE 88 E5 B1 8C

然后套用之前UTF-8編碼介紹章節(jié)中總結(jié)出的規(guī)律,就不難發(fā)現(xiàn)這6個(gè)字節(jié)的數(shù)據(jù)符合UTF-8編碼規(guī)則。如果整個(gè)數(shù)據(jù)流都符合這個(gè)規(guī)則的話,我們就能大膽假設(shè)亂碼之前的編碼字符集是UTF-8
第3步 解碼

然后我們就能拿著E5BE88E5B18C用UTF-8解碼,查看亂碼前的文字了。當(dāng)然我們可以不查表直接通過(guò)SQL獲得結(jié)果:

  1. mysql [localhost] {msandbox} ((none)) > select convert(0xE5BE88E5B18C using utf8); 
  2. +------------------------------------+ 
  3. convert(0xE5BE88E5B18C using utf8) | 
  4. +------------------------------------+ 
  5. | 很屌 | 
  6. +------------------------------------+ 
  7. 1 row in set (0.00 sec) 

常見(jiàn)問(wèn)題處理之Emoji

所謂Emoji就是一種在Unicode位于\u1F601-\u1F64F區(qū)段的字符。這個(gè)顯然超過(guò)了目前常用的UTF-8字符集的編碼范圍\u0000-\uFFFF。Emoji表情隨著IOS的普及和微信的支持越來(lái)越常見(jiàn)。下面就是幾個(gè)常見(jiàn)的Emoji:

[[129200]]

那么Emoji字符表情會(huì)對(duì)我們平時(shí)的開(kāi)發(fā)運(yùn)維帶來(lái)什么影響呢?最常見(jiàn)的問(wèn)題就在于將他存入MySQL數(shù)據(jù)庫(kù)的時(shí)候。一般來(lái)說(shuō)MySQL數(shù)據(jù)庫(kù)的默認(rèn)字符集都會(huì)配置成UTF-8(三字節(jié)),而utf8mb4在5.5以后才被支持,也很少會(huì)有DBA主動(dòng)將系統(tǒng)默認(rèn)字符集改成utf8mb4。那么問(wèn)題就來(lái)了,當(dāng)我們把一個(gè)需要4字節(jié)UTF-8編碼才能表示的字符存入數(shù)據(jù)庫(kù)的時(shí)候就會(huì)報(bào)錯(cuò):ERROR 1366: Incorrect string value: '\xF0\x9D\x8C\x86' for column 。

如果認(rèn)真閱讀了上面的解釋,那么這個(gè)報(bào)錯(cuò)也就不難看懂了。我們?cè)噲D將一串Bytes插入到一列中,而這串Bytes的第一個(gè)字節(jié)是\xF0意味著這是一個(gè)四字節(jié)的UTF-8編碼。但是當(dāng)MySQL表和列字符集配置為UTF-8的時(shí)候是無(wú)法存儲(chǔ)這樣的字符的,所以報(bào)了錯(cuò)。

那么遇到這種情況我們?nèi)绾谓鉀Q呢?有兩種方式:升級(jí)MySQL到5.6或更高版本,并且將表字符集切換至utf8mb4。第二種方法就是在把內(nèi)容存入到數(shù)據(jù)庫(kù)之前做一次過(guò)濾,將Emoji字符替換成一段特殊的文字編碼,然后再存入數(shù)據(jù)庫(kù)中。之后從數(shù)據(jù)庫(kù)獲取或者前端展示時(shí)再將這段特殊文字編碼轉(zhuǎn)換成Emoji顯示。第二種方法我們假設(shè)用-*-1F601-*-來(lái)替代4字節(jié)的Emoji,那么具體實(shí)現(xiàn)python代碼可以參見(jiàn)Stackoverflow上的回答。

責(zé)任編輯:chenqingxiang 來(lái)源: linux.cn
相關(guān)推薦

2015-03-12 17:01:33

MySQLMySQL亂碼編碼轉(zhuǎn)換

2020-12-17 06:48:21

SQLkafkaMySQL

2019-09-16 09:14:51

2019-04-01 14:59:56

負(fù)載均衡服務(wù)器網(wǎng)絡(luò)

2021-09-07 09:40:20

Spark大數(shù)據(jù)引擎

2022-06-16 07:31:41

Web組件封裝HTML 標(biāo)簽

2024-06-19 09:58:29

2023-04-12 11:18:51

甘特圖前端

2022-03-23 09:32:38

微服務(wù)容器Kubernetes

2012-07-10 01:22:32

PythonPython教程

2015-09-06 09:22:24

框架搭建快速高效app

2023-11-30 10:21:48

虛擬列表虛擬列表工具庫(kù)

2024-05-13 09:28:43

Flink SQL大數(shù)據(jù)

2024-03-04 15:19:52

Python編程內(nèi)建函數(shù)

2023-07-15 18:26:51

LinuxABI

2009-10-09 14:45:29

VB程序

2024-11-07 16:09:53

2022-08-26 09:01:07

CSSFlex 布局

2023-09-26 22:12:13

數(shù)據(jù)倉(cāng)庫(kù)Doris

2020-12-11 09:40:10

DevOpsCICD
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)