在平常Ulink活動跟進過程當中,有時活動頁面打開的時候會遇到亂碼的狀況(以下圖所示),因而就想亂碼究竟是怎麼產生的,遇到亂碼的狀況應該怎麼去解決,帶着這些問題,我去查閱了相關的資料,在這裏整理成文章分享給你們,但願對你們有所幫助。html
咱們都知道,計算機是隻認識0和1的二進制數的,因此無論是字母,漢字,或者符號,都是以某種編碼方式轉換成二進制數據存放在計算機中,須要顯示的時候,就用相同的編碼方式把二進制數據解碼出來就能夠了。那麼這就很好理解亂碼的產生了,若是咱們用A編碼方式將字符進行編碼,而後用B編碼方式來解碼,解碼出來的就確定是亂碼。咱們用一個比較風趣的故事能夠很形象的說明這個問題:有一天你帶着女友去見一個老外朋友,見面時老外很熱情的讚揚道 your girl friend is so beautiful,而後你本着中華名族謙遜有禮的優良傳統,回答「where」,這時你的老外朋友應該就是這個表情了…編程
這個故事中,你想表達的「哪裏」這個內容用博大精深中文編碼後是謙虛的意思,可是老外用他本身的思惟去解碼後就是哪裏的本意,固然會不明白什麼意思了。瀏覽器
要搞清楚亂碼的問題,我以爲應該從這些很容易混淆的基本概念提及。譬如什麼是字符,字符編號,字符集等。微信
字符是具備語義值的最小文本單位,包括字母、數字、運算符號、標點符號和其餘符號,以及一些功能性符號,字符也是數據結構中最小的數據存取單位,有的人可能會有一些誤解,認爲英文字母或者特殊符號佔一個字節,而中文漢字是佔2個字節,其實這種想法不許確的,字符在計算機中所佔的字節數和它的字符編碼有關,在GB2312字符編碼中,一個漢字是佔2個字節,可是在UTF-8字符編碼中,是佔3個字節,因此在不知道哪一種字符編碼的時候說字符佔多少個字節的說法是不夠嚴謹的。markdown
字符編號是用來表示一個字符的號碼,相似咱們的身份證號碼,具備不可重複性,同一個字符在不一樣的字符集中,他的字符編號也是不同的,這個也很好理解,就像你在中國的護照號碼和你移民到了美國的護照號,你仍是你,可是護照號已經不同了。網絡
字符集是多個字符的集合,字符集種類較多,每一個字符集包含的字符個數不一樣,常見字符集有:ASCII字符集、GB2312字符集、Unicode字符集等。不一樣的字符集包含的字符個數也不相同,好比ASCII字符集收錄了128個字符,而GB2312收錄了7445個字符。日常你們所說的字符集準確的講應該叫編號字符集,它是指用字符編號來標示某個字符集中每個字符後生成的字符集合,聽着有點繞,爲啥要給字符加個編號呢?主要仍是能方便定位找到某個指定的字符,這個和每一個人生出後都會起一個姓名同樣。數據結構
字符編碼也稱字集碼,是把字符集中的字符,編碼後生成指定集合中某一對象(例如:比特模式、天然數序列、8位組或者電脈衝),以便文本在計算機中存儲和經過通訊網絡的傳遞。這是百度百科對它的專業定義,本人的理解是字符集中的字符標號和實際存儲的二進制數據之間的映射關係,譬如在ASCII字符編碼中字母A的編號是65,因此它存入計算機時的值就是’01000001’。編程語言
有些人比較容易混淆字符集和字符編碼這兩個概念,通俗的講,字符集是用字符編號標示後的字符集合,而字符編碼是指字符用什麼方式轉化成二進制數據的規則。編碼
編碼是信息從一種形式或格式轉換爲另外一種形式的過程,也稱爲計算機編程語言的代碼(簡稱編碼)。用預先規定的方法將文字、數字或其它對象編成數碼,或將信息、數據轉換成規定的電脈衝信號。通俗的理解就是將字符按必定的規則轉換成二進制數據。spa
與編碼相對,解碼是一種用特定方法,把數碼還原成它所表明的內容或將電脈衝信號、光信號、無線電波等轉換成它所表明的信息、數據等的過程。解碼是將接受到的符號或代碼還原爲信息的過程,與編碼過程相對應。
文章開頭也說到數據都是以二進制的形式存儲在計算機中,那哪些二進制數據表示哪些字符,每一個人都有一套本身的規則,這樣就不能互相通訊,爲了解決這個問題,美國相關標準化組織就出臺了ASCII(American Standard Code for Information Interchange,美國信息交換標準代碼)編碼,統一了二進制數據對應字符的規則,ASCII字符集一共包含128個字符(0-127)。其中有33個控制字符(不可顯示字符),52個英文字母(包括大小寫),0~9十個阿拉伯數字,其他爲一些特殊符號。它使用一個字節的後7位來表示某個字符,對應的二進制編碼是0000 0000 ~ 0111 1111,其中最高位通常爲0,但有時也被用做一些通信系統的奇偶校驗位,就像上面舉例的大寫字母A的二進制編碼就是’01000001’。
隨着計算機技術的慢慢發展,人們發現ASCII字符集的128個字符已經不能知足他們的需求了,因而ISO8859-1字符集就誕生了,它共有256個字符,向下兼容ASCII,編碼範圍是0x00-0xFF,其中0x00-0x7F之間徹底和ASCII一致,0x80-0x9F之間是控制字符,0xA0-0xFF之間是文字符號。它屬於西歐語系中的一個字符集,支持希臘語、丹麥語、阿拉伯語等,和ASCII字符集同樣,ISO8859-1字符集也是使用默認的字符編碼來把字符編號轉換成二進制數據存儲在計算機中。
GB2312字符集全稱爲《信息交換用漢字編碼字符集·基本集》,由原中國國家標準總局發佈,1981年5月1日實施。計算機到中國的時候,已經沒有空餘的字節空間存放中文了,並且中文有那麼多漢字,怎麼也放不下,因此只好另外再新建一種字符集,因而GB2312字符集就產生了,它使用兩個字節來表示一個漢字,前面的一個字節(高字節)使用0xA1-0xF7這個區間,後面一個字節(低字節)使用0xA1-0xFE這個區間,這樣咱們就能夠組合出大約存放7000多個字符的空間了。其中包括6763個漢字(一級漢字3755個,二級漢字3008個),已經能夠覆蓋99.75%的漢字使用頻率,基本知足了漢字的計算機處理須要。GB2312字符集還把符號、羅馬希臘字母、日文假名都編進去了,就連ASCII 裏原本就有的數字、標點、字母都從新編了兩個字節長的編碼,這就是常說的"全角"字符,而原來在127號如下的那些就叫"半角"字符。全角和半角經過高字節的最高位是1仍是0來判斷,1表示全角,按照兩個字節來解碼,0表示半角,按照1個字節來解碼。那這麼多字符是怎麼排列的呢?GB2312中對所收漢字進行了「分區」處理,每一個區含有94個漢字/符號。這種表示方式也稱爲區位碼。各區包含的字符以下:01-09區爲特殊符號;16-55區爲一級漢字,按拼音排序;56-87區爲二級漢字,按部首/筆畫排序;10-15區及88-94區則未有編碼。下面舉一個具體例子說明是怎麼存放的。GB2312編碼是將區碼和位碼分別加上160(十進制)的偏移量以後(由於要兼容ASCII碼),再轉化成二進制存放到計算機中的,譬如「有」字的區位碼是5148,區碼51和位碼48分別加上160之後就是211和208,因此它在計算機中的存儲值就是’1101001111010000’。
計算機到了各個國家後,每一個國家都各自搞了一套能兼容ASCII字符集但又不能互相兼容的編碼方案,這樣就會致使一個國家的二進制編碼到了另一個國家不能被正常解碼的問題,對數據傳輸帶來很大的障礙(就像古代秦國統一其餘六國後也面臨着這個問題),這時候就迫切須要有一種可以容納世界上全部語言和符號的字符集,因而Unicode字符集就這樣萬衆期待的誕生了。在Unicode字符集中,每個字符都有一個獨一無二的編碼,這也正好印證了它的名字,Unique(惟一)和code(編碼)二者的結合就成了Unicode。它一共有17個平面(0到16),每一個平面裏又分了不少頁,每一頁裏又有不少行,而咱們的字符就按照各自的字符編號,靜靜的存放在每一行裏。目前17個平面中目前只用到0號、1號、2號、3號和14號平面,其中咱們的漢字在0號、2號和3號平面,其它文字在0號、1號和14號平面;譬如「當」字就存放在第0號平面,第6頁,第96行,他的編號就是5F53。
Unicode的產生標誌着字符編碼領域進入了一個全新時代,不只是由於它存儲了世界上全部的字符,並且它還把字符集和字符編碼的定義嚴格的區分開來,在Unicode誕生以前,全部的字符集都是和字符編碼捆綁在一塊兒的,這種方式的缺點在與字符和字節流之間的耦合度過高,致使它的可擴展性不強。而Unicode考慮到了這點,能夠有不一樣的字符編碼(UTF-八、UTF-16等),也就是說,決定最終字節流的是字符編碼,譬如對字母「A」進行UTF-8編碼,獲得的字節流是0x41,用UTF-16進行編碼,獲得的倒是0x00 0x41。下圖是GB2312和Unicode的對比,很好的說明了這個問題。
在Unicode衆多編碼方式中,咱們最多見到的就是UTF-8,下面就詳細說一說這個編碼方式。就像上面所說,字符編碼實際上是從字符編號到實際存儲二進制字節流的映射,下面這張表能夠分析它是怎麼實現這個映射關係的。表中x表明序號部分,把各個字節中全部x拼接在一塊兒就組成了在Unicode字符集中的字符編號,其中第一個字節能夠是0,110,1110開頭,這樣以此類推,可是從第二個字節開始,後面每一個字節都只能以10開頭,不一樣的數字開頭分別表示的意思以下:
由於篇幅關係,這裏舉一個最多見的HTML頁面的亂碼場景,咱們項目指定使用UTF-8編碼,可是在html文件中,咱們使用GBK編碼,用瀏覽器運行後就出現了亂碼。
下面咱們來具體分析一下是怎麼產生這些亂碼的,文章一開始也說了,亂碼的產生其實就是編碼和解碼使用的方式不一致產生的,UTF-8和GBK都很好的兼容了ASCII字符集,因此它們對單字節的英文字母或者數字解碼時不會有什麼差異,可是對中文漢字就不同了,對UTF-8而言,一個漢字佔3個字節,因此對原本要顯示的這段文本編碼後產生的二進制字節流以下圖左邊部分所示,而對GBK而言,一個漢字是佔兩個字節的,第一個字節的十進制值大於128,則認爲這個是表示中文漢字,會將它和後面一個字節連起來一塊兒解碼,因此這段文本用GBK解析後從新分組排列就變成了下圖右邊部分所示,除了第五組和第十組是一個字節,其餘每組都是兩個字節(由於它們的第一個字節的十進制值都大於128),其中第五組是由於第一個字節小於128,第十組是由於後面沒有字節了。咱們經過每組的二進制值在GBK字符集中找到它對應的位置,查到的具體字符和瀏覽器顯示的是一致的。
若是想讓網頁顯示正常的文本其實很簡單,只要將html中的編碼改爲UTF-8就能夠了。
從第一個字符集的誕生到後面不斷有新的字符集產生,其實都是由於隨着計算機的發展,原有的字符集知足不了當前的需求的緣由,而後纔會有不一樣的編碼方式來編碼和解碼,最終產生的亂碼,從上面的例子中也不難看出,其實相似咱們日常看一句句子,用不一樣的斷句方式,可能會產生徹底不同的句意,但願經過這篇文章,能夠幫你們把字符集,字符編碼理清楚,遇到亂碼的時候,只要能分析好每次編碼和解碼使用的方式是否一致,這樣亂碼的問題天然會迎刃而解了。
更多精彩內容,盡請關注騰訊VTeam技術團隊微信公衆號和視頻號
原做者:裘維清
未經贊成,禁止轉載!