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

NO IMAGE

知其然,知其所以然,徹底搞懂編碼,搞定亂碼

亂碼問題是所有運維職業生涯中都會遇到的問題,本篇文章帶你探究背後的原理以及解決的技巧

字符編碼

我們知道計算機只認識二進制數據,其他格式的數據都需要轉換成二進制才能被計算機處理,也就是說我們在計算機上看到的文本、視頻、可執行程序等格式的文件,最終都會轉換成二進制數據交給計算機處理

計算機中最小的數據單位是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 007F0xxxxxxx
0000 0080 – 0000 07FF110xxxxx 10xxxxxx
0000 0800 – 0000 FFFF1110xxxx 10xxxxxx 10xxxxxx
0001 0000 – 0010 FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

對於運維咖啡吧的字,其Unicode編碼為U+54965496在上邊的第三行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環境時指定的語言編碼,例如LANGLC_*這些變量設置的編碼,XSHELL之類終端編碼就是這類終端軟件設置的編碼

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

所有遇到的亂碼問題都仔細檢查以上三方編碼是否一致,就可以順利解決了,同時也建議在工作中制定相應的規範,減少亂碼的發生

處理技巧

1.臨時切換命令輸出語言

正常情況下命令的輸出結果都遵循系統設置的語言編碼,例如

[email protected]:~# echo $LANG
zh_CN.UTF-8
[email protected]:~# date
2020年 03月 04日 星期三 19:00:55 HKT
[email protected]:~# 
[email protected]:~# 
[email protected]:~# export LANG=en_US.UTF-8
[email protected]:~# echo $LANG
en_US.UTF-8
[email protected]:~# date
Wed Mar  4 19:01:21 HKT 2020

運維腳本中,我們希望所有系統執行相同命令的時候輸出的結果一致,不要因為字符集不同而產生不同的結果,那麼如可處理呢?在命令前添加LC_ALL=C

[email protected]:~# date
2020年 03月 04日 星期三 19:05:58 HKT
[email protected]:~# 
[email protected]:~# 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以後版本中才支持,用法為--iconv=<LOCAL>,<REMOTE>,需要注意的是,本地兩個目錄之間同步時LOCAL表示的是源目錄的文件名編碼,通過網絡同步時LOCAL表示本地編碼


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

相關文章推薦閱讀:

相關文章

遊記|一場說走就走的旅行,突破常規,驚喜不斷

Probius:一個功能強大的自定義任務系統

Djangomodel重寫save方法及update踩坑記錄

GithubPages訪問太慢?通過Netlify免費加速