天天看點

【Coding】聊聊字元編碼那些事兒

Part.0

目錄

目錄

一、進制轉換與ASCII碼

  1. 進制轉換
  2. 檔案的存儲形式
  3. ASCII碼

二、Unicode和UTF-8編碼

  1. 中文編碼
  2. Unicode
  3. UTF編碼

三、Base64編碼

  1. base64
  2. 編碼規則

四、URL編碼

  1. URL
  2. URL編碼

五、結語

【Coding】聊聊字元編碼那些事兒

Part.1

進制轉換與ASCII碼

進制轉換

我們知道,計算機中的所有資訊都是以二進制的方式表示的。

二進制數中的一個“0”或者一個“1”,大小為一個bit,又稱為1位(b);

一個8位的二進制數,大小為一個Byte,又稱為1位元組(B)。

是以,1KB=1024B(位元組)=1024*8b(位)

包括二進制在内,常用的進制表示方法如下:

  • 二進制:以0b作為字首,0b1011。
  • 八進制:以0o作為字首,0o174。
  • 十六進制:以0x作為字首,0x1e。

在Python中,使用print函數輸出,會自動轉換為十進制數,如下:

【Coding】聊聊字元編碼那些事兒

當然,也可以使用int()函數來進行轉換,不需要帶字首:

【Coding】聊聊字元編碼那些事兒

注意:被轉換的資料必須以"字元串形式?imageView2/2/w/1620"輸入,後面跟上所屬的進制。

python内還提供了三個函數,讓我們來将十進制數轉換為其他進制,如下:

【Coding】聊聊字元編碼那些事兒
【Coding】聊聊字元編碼那些事兒

檔案的存儲形式

說完進制轉換,我們再來看看檔案的存儲形式。

計算機中的檔案,分為文本檔案和二進制檔案。

文本檔案中存放的資料在使用者讀取時可以按照編碼類型還原成字元形式,我們可以直接打開,如下:

【Coding】聊聊字元編碼那些事兒

二進制檔案中存放的資料則不能還原成字元形式,像圖檔、視訊、音頻、可執行檔案等都屬于是二進制檔案。

我們使用cat打開一個圖檔檔案,由于不能還原,是以隻能看到亂碼:

【Coding】聊聊字元編碼那些事兒

//是以圖檔隻能用看圖軟體去打開,裡面的二進制數代表的是像素點。

相同的,文本檔案也不可以直接執行,例如我們寫好的c文本代碼,必須編譯成可執行的二進制檔案才可以,編譯完成後,直接打開就會變成亂碼。

那麼怎麼檢視二進制檔案的二進制數呢?可以使用010editor等軟體。例如打開上面的圖檔:

【Coding】聊聊字元編碼那些事兒

但是在二進制檔案中,也可以插入文本資訊,使用010editor直接編輯:

【Coding】聊聊字元編碼那些事兒

通過文本檔案打開該檔案,就可以看到我們添加的代碼:

【Coding】聊聊字元編碼那些事兒

添加了代碼,但圖檔依舊可以正常顯示,圖檔馬就是這樣來的。

【Coding】聊聊字元編碼那些事兒

ASCII碼

首先,ASCII碼是一種字元編碼方式,我們為什麼要對字元進行編碼呢?

字元編碼就是将我們人類所使用的每一個字元都對應到一個唯一的數字上,使得我們的字元可以用二進制數的方式被計算機識别。

ASCII碼,全稱美國标準資訊交換碼,一共包含128個字元,對英文字母以及一些常用的符号進行了編碼。

ASCII使得每個字元在計算機内部都對應了一個8位的二進制數,大小為1個位元組。

在Linux中,使用man指令可以檢視ASCII表:

【Coding】聊聊字元編碼那些事兒

//例如,字元"A?imageView2/2/w/1620"的ASCII碼是65,對應八進制數101,十六進制數則是40

ASCII碼中的128個字元分成了兩個部分:

  • 非列印字元: 0~32,規定了一些特殊的用途, 當終端裝置或者列印機遇上這些字元時, 就要做一些約定的動作,比如空格、換行等。
  • 可列印字元: 33~127,也就是可以顯示輸出的字元,包括了所有的大小寫英文字母、數字、标點符号等。

在python2中,使用ord()函數,可以得出字元的ASCII碼(十進制)

【Coding】聊聊字元編碼那些事兒

使用chr()函數,可以得出ASCII碼所對應的字元:

【Coding】聊聊字元編碼那些事兒

ASCII碼隻包含128個字元,但随着計算機的普及,遠遠不能滿足現有需求。

是以後續各個國家又各自對ASCII碼進行了擴充,8位ASCII碼隻有128個字元,擴充的ASCII碼又增加了128個字元。

由于每個國家擴充的内容不同,是以擴充ASCII碼是一種非國際标準。

【Coding】聊聊字元編碼那些事兒

Part.2

Unicode和UTF-8編碼

中文編碼

前面說到,擴充ASCII碼最多也隻能表示128+128個字元。

然而中文的漢字量遠不是ASCII碼可以表示的,是以采用了新的GB2312編碼來表示中文,即使用2個位元組來表示一個漢字。

GB2312還對ASCII碼中原有的字元也按照2個位元組來重新進行編碼,稱為全角字元,原先的ASCII碼稱為半角字元。

後面在GB2312的基礎之上新加了20000多個字元(包括繁體字),形成了GBK編碼。

再後來在GBK的基礎之上又新加了很多少數民族的字元,稱為GB18030編碼。

中國台灣和中國香港使用的另外一套漢字編碼方案,稱為BIG5碼。

以上中文編碼方式都是國标,非國際标準。

【Coding】聊聊字元編碼那些事兒

Unicode

我們要打開一個文本檔案,必須知道它的編碼方式,如果用錯誤的編碼方式解讀,就會出現亂碼。

是以,國際标準化組織ISO決定廢除所有的地區性編碼方案,重新制訂一個可以包含世界上所有文化、所有字元的編碼方案,這就是Unicode編碼,也稱為統一碼、萬國碼。

值得注意的幾點:

  • 從2007年開始,Unicode已逐漸取代ASCII成為了通用編碼。
  • Python3預設采用Unicode編碼,Python2預設采用ASCII碼。
  • 對于英文字母和數字,ASCII和Unicode是一緻的。
  • 對于漢字,ASCII碼無法正常顯示,是以python2開頭要指定編碼方式為utf-8,python3則不需要指定。
  • ord()函數在Python3中檢視的是Unicode編碼,不再是ASCII碼了。

以“中國”為例,我們來看看Unicode的表示方法。

首先檢視"中國"的Unicode十進制編碼和十六進制編碼:

【Coding】聊聊字元編碼那些事兒

十六進制可以寫成中国

十進制可以寫成中国

//使用浏覽器打開,會被浏覽器自動解碼,顯示成中文;也可以寫成 \U4e2d\U56fd 和\U+4e2d\U+56fd兩種方式,就不會被浏覽器自動解碼。

【Coding】聊聊字元編碼那些事兒

UTF編碼

Unicode隻是對所有的字元進行了編碼,但沒有規定該如何存儲和傳輸這些字元。

比如對于Unicode編碼的字母a,在計算機中該如何存儲?ASCII碼的話使用1個位元組來進行存儲,Unicode呢?

Unicode并沒有規定使用幾個位元組,隻是确定一套編碼方式。

還需要使用UTF編碼(Unicode Transformation Format)來存儲,即Unicode的轉換格式。

UTF包含UTF-8、UTF-16、UTF-32。

以UTF-8為例:

【Coding】聊聊字元編碼那些事兒
  • UTF-8是一種可變長的編碼方式,使用1~6個位元組表示一個符号。
  • 英文字母被編碼成1個位元組,漢字通常是3個位元組,生僻字元被編碼成4~6個位元組。
  • 動态的編碼方式可以有效減小存儲所占的空間。
  • 如果位元組的第一位是0,則這個位元組單獨就是一個字元;
  • 如果位元組的第一位是1,連續有多少個1,就表示字元占用多少個位元組。
【Coding】聊聊字元編碼那些事兒

如圖,UTF-8就是Unicode的一種實作方式,當資料在計算機記憶體中被處理時,統一使用Unicode編碼。當需要儲存到硬碟或者需要傳輸的時候,就轉換為UTF-8編碼。

【Coding】聊聊字元編碼那些事兒

Part.3

Base64編碼

Base64

ASCII碼是把字元轉換為二進制數,Base64則是把二進制數編碼成字元。

Base64編碼的作用:

  • 某些系統中隻能使用ASCII字元,Base64是将非ASCII字元的資料轉換成ASCII字元的一種方法。
  • 在ASCII碼中有很多不可見的控制字元,這些控制字元不利于在網上傳輸,Base64隻使用了ASCII碼中一部分可見字元。
  • Base64包括大小寫字母各26個、10個數字、加号+、斜杠/,一共64個字元。
  • 除了這64個字元之外,在Base64編碼中可能還會使用等号=作為字尾填充,因為base64編碼後的字元長度必須為4的倍數。
【Coding】聊聊字元編碼那些事兒

編碼規則

Base64編碼要求把3個8位位元組(3*8=24)轉換為4個6位的位元組(4*6=24),之後在6位的前面補兩個0,形成8位一個位元組的形式。

如果剩下的字元不足3個位元組,則用0填充,輸出字元使用"="号,是以文本末尾可能出現"="号。

根據編碼表進行轉換,Base64有自己的編碼表:

【Coding】聊聊字元編碼那些事兒

以 s13為例,進行base64編碼:

  • 轉換為ASCII碼:115 49 51
  • 轉換為二進制格式:01110011 00110001 00110011
  • 重新分為4組:011100 110011 000100 110011
  • 開頭補0轉換為十進制:28 51 4 51
  • 根據編碼表得到base64編碼:c z E z

如何确定一個字元串是否是base64編碼呢?

  • 字元串隻可能包含A-Z,a-z,0-9,+,/,=字元。
  • =隻會出現在字元串最後,最多三個,當然也可能沒有。
  • 字元個數是4的倍數。

在linux中快速編碼(-n表示不添加換行符):

【Coding】聊聊字元編碼那些事兒

在linux中解碼:

【Coding】聊聊字元編碼那些事兒

使用python2編碼:

【Coding】聊聊字元編碼那些事兒

使用python2解碼:

【Coding】聊聊字元編碼那些事兒

使用python3編碼(加b表示bytes類型):

【Coding】聊聊字元編碼那些事兒

使用python3解碼:

【Coding】聊聊字元編碼那些事兒

base64對二進制數進行編碼,是以主要處理對象是二進制檔案,對檔案進行編碼:

【Coding】聊聊字元編碼那些事兒

還原檔案需要用一些線上轉換工具,也可以直接粘貼到浏覽器中,加上識别頭部:

【Coding】聊聊字元編碼那些事兒

Base編碼除了base64以外,還有其他的如下:

  • Base16:由0~9、A~F組成,實際上就是十六進制數。
  • Base32:由大寫字母、234567組成。
  • Base85:由0-9、A-Z、a-z、以及23個字元組成:!#$%&()*+-;?@^_`{|}~
【Coding】聊聊字元編碼那些事兒

Part.4

URL編碼

URL

URL(Uniform Resource Locator,統一資源定位符)是一種可以為網際網路中所有資源進行統一定位的機制。

常見格式:

協定名://主機名(IP位址):端口/路徑?請求

【Coding】聊聊字元編碼那些事兒

?參數名1=參數值1&參數名2=參數值2,可以通過Get方式傳參,如下:

【Coding】聊聊字元編碼那些事兒

向後端傳遞user=admin,passwd=admin

【Coding】聊聊字元編碼那些事兒

URL編碼

我們為什麼需要URL編碼呢?

試想一下,如果要傳輸的參數值中包含“=”或“&”這種特殊字元該怎麼辦?

解決方法就是使用URL編碼。

URL編碼格式:%十六進制ASCII碼

例如&的URL編碼就是%26,通過編碼傳遞&符:

【Coding】聊聊字元編碼那些事兒

并不是所有字元都需要URL編碼,規則如下:

  • 傳輸标準ASCII字元無需URL編碼,傳輸容易引起歧義的字元時,就要進行URL編碼。
  • 如果在URL中對一些ASCII标準字元進行了URL編碼,那麼浏覽器會自動将其解碼。
  • 特殊字元浏覽器不會進行自動解碼。
  • 如果要在URL中傳送中文,也必須要經過URL編碼。
  • +比較特殊,它在URL中會被視為空格。

有辦法繞過浏覽器的自動解碼呢?

答案是:可以對字元進行二次URL編碼。

【Coding】聊聊字元編碼那些事兒