1. 程式人生 > >關於編碼你必須知道的知識和技巧

關於編碼你必須知道的知識和技巧

> 知其然,知其所以然,徹底搞懂編碼,搞定亂碼 亂碼問題是所有運維職業生涯中都會遇到的問題,本篇文章帶你探究背後的原理以及解決的技巧 ## 字元編碼 我們知道計算機只認識二進位制資料,其他格式的資料都需要轉換成二進位制才能被計算機處理,也就是說我們在計算機上看到的文字、視訊、可執行程式等格式的檔案,最終都會轉換成二進位制資料交給計算機處理 計算機中最小的資料單位是bit,也叫二進位制位,每一個bit都有0和1兩種狀態,最早的計算機在設計時採用了8個bit作為一個位元組byte,所以一個位元組能表示的最大整數就是二進位制的`11111111`,等於十進位制的`255`,想要表示更大的整數就必須要用多個位元組,例如兩個位元組可以表示最大的整數就是二進位制的`1111111111111111`,等於十進位制的`65535` 由於計算機是由美國人發明的,在1967年美國人制訂了一套字元編碼規範,規定了包含大小寫字母、數字和一些符號共計128個字元與二進位制數字的對應關係,例如回車Enter是二進位制是`00001101`,等於十進位制的13,大寫字母A是二進位制`01000001`,等於十進位制的65,這一套字元編碼被稱為**ASCII**碼,一直沿用至今 英文比較簡單,用128個符號編碼就夠了,但是用來表示中文就不夠了,單單漢字就有超過8萬個,所以就有了針對中文的編碼標準出現,例如我們經常見到的`GB2312`,使用兩個位元組表示一個漢字,理論上最多可以表示65535個 世界上有上百種語言,每種語言都有自己的編碼標準,例如韓文編碼`EUC_KR`,日文編碼`Shift_JIS`,俄文編碼`KOI8-R`,為了促進網際網路的發展,**Unicode**編碼應運而生,Unicode編碼又稱萬國碼、國際碼,它對世界上大部分的文字系統進行了整理,使每一個文字元號都有獨一無二的編碼表示,當前Unicode最新的版本為2019年5月公佈的`12.1.0`,已經收錄超過13萬個字元,很明顯2個位元組已經無法保證所有字元都獨一無二了,實際上最新的Unicode規定可以佔用4位元組來表示一個字元,理論上最多能表示2的31次方共計2147483648個字元 Unicode雖然能夠解決不同編碼出現的問題,使得電腦可以用更為簡單的方式來呈現和處理文字,但同時存在著浪費儲存和頻寬的問題,例如大寫字母A,用ASCII碼錶示是`01000001`,只需要佔一個位元組,如果轉換成2個位元組的Unicode編碼就變成了`0000000001000001`,這就極大的浪費了儲存空間,同時對於網路傳輸消耗也相應增大 為了解決Unicode的問題,**UTF-8**編碼方式出現了,UTF-8是一種可變長的編碼方式,它通過字首碼的方式使Unicode編碼變成了可變長度,關於UTF-8的具體字首規則簡單總結為2點如下: 1. 單位元組的字元,位元組的第一位設為0,後邊7位為Unicode碼。對於英語字母,UTF-8編碼和ASCII碼完全相同 2. n個位元組的字元(n>1),第一個位元組的前n位設為1,第n+1位設為0,後面位元組的前兩位都設為10,這n個位元組的其餘空位填充該字元unicode碼,不足用0補足 那就形成了如下的UTF-8編碼規則,其中的`x`表示的就是要用Unicode填充可用的編碼位 Unicode符號範圍(16進位制) | UTF-8編碼方式(2進位制) --- | --- 0000 0000 - 0000 007F | 0xxxxxxx 0000 0080 - 0000 07FF | 110xxxxx 10xxxxxx 0000 0800 - 0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 0001 0000 - 0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 對於運維咖啡吧的`咖`字,其Unicode編碼為`U+5496`,`5496`在上邊的第三行`0000 0800 - 0000 FFFF`的範圍內,因此帶入公式計算如下 ``` 0101 010010 010110 (最前邊的0便是unicode不足,用0代替) 1110xxxx 10xxxxxx 10xxxxxx (模板,由於3位元組,所以是上邊第三行) ------------------------------------------------------------------ 11100101 10010010 10010110 (結果,UTF-8的二進位制值) ``` 根據上邊的計算結果得出運維咖啡吧的`咖`字UTF-8編碼是`111001011001001010010110`,轉換為16進製為`E59296` 這便是Unicode與UTF-8的區別,UTF-8可變長就是這麼可變長的,對於英文字母來說UTF-8只佔一個位元組,而對於漢字來說他可能就佔了3個位元組 ## 終端亂碼 從上邊的編碼介紹中我們已經知道了不同編碼的存在,那麼想要檢視一個檔案,就必須知道他的編碼方式,用錯誤的編碼方式開啟檔案就會出現亂碼。 linux下可以通過`file`命令檢視檔案的編碼方式 ``` # file ops-coffee.cn ops-coffee.cn: UTF-8 Unicode text ``` 工作中我們在XSHELL之類的終端中檢視檔案時出現的亂碼就是系統或檔案儲存的中文編碼與終端設定的編碼不一致,從而導致解碼錯誤。這裡涉及到三方編碼: 1. 檔案內容或檔名 2. SHELL環境的語言編碼 3. XSHELL之類的終端編碼 需要保持三方編碼統一,才不會有亂碼的出現,其中SHELL環境的語言編碼指的是登陸伺服器的SHELL環境時指定的語言編碼,例如`LANG`、`LC_*`這些變數設定的編碼,XSHELL之類終端編碼就是這類終端軟體設定的編碼 ![](https://blz.nosdn.127.net/sre/images/20200304.01.png) 所有遇到的亂碼問題都仔細檢查以上**三方編碼**是否一致,就可以順利解決了,同時也建議在工作中制定相應的規範,減少亂碼的發生 ## 處理技巧 1.臨時切換命令輸出語言 正常情況下命令的輸出結果都遵循系統設定的語言編碼,例如 ``` root@ops-coffee:~# echo $LANG zh_CN.UTF-8 root@ops-coffee:~# date 2020年 03月 04日 星期三 19:00:55 HKT root@ops-coffee:~# root@ops-coffee:~# root@ops-coffee:~# export LANG=en_US.UTF-8 root@ops-coffee:~# echo $LANG en_US.UTF-8 root@ops-coffee:~# date Wed Mar 4 19:01:21 HKT 2020 ``` 運維指令碼中,我們希望所有系統執行相同命令的時候輸出的結果一致,不要因為字符集不同而產生不同的結果,那麼如可處理呢?在命令前新增`LC_ALL=C` ``` root@ops-coffee:~# date 2020年 03月 04日 星期三 19:05:58 HKT root@ops-coffee:~# root@ops-coffee:~# LC_ALL=C date Wed Mar 4 19:06:05 HKT 2020 ``` 這裡之所以用`LC_ALL`是因為在LOCALE標準中,`LC_ALL`優先順序最高:`LC_ALL>LC_*>LANG` 2.批量轉換檔名編碼 有時候我們會遇到檔名或者目錄名亂碼的問題,尤其是在不同型別系統之間傳輸時,可以藉助`rsync`實現批量轉換檔名或目錄名的編碼 ``` rsync -av --iconv=GBK,UTF8 /www/ /nav/ ``` iconv模組在rsync的3.0以後版本中才支援,用法為`