日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

字符编码简介

發布時間:2024/9/30 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 字符编码简介 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

???????

目錄

一、ascii編碼

二、擴展ascii編碼

三、多字節編碼(multi bytes)

四、寬字符編碼(wide char)

五、unicode編碼

六、utf-8編碼

七、結語


?????????大家好,我是略游。本文的目的是講清楚,字符編碼的今生來世。看完后你會對字符編碼的規則有一個宏觀印象。

一、ascii編碼

????????字符(character)是計算機與人交互的媒介,人雖然可以看懂二進制串,但文字是更加直觀的。所以需要用數字來表示字符,字符與數字的對應關系就叫編碼(coding)。

????????由于計算機發源于美國,一開始不需要顯示其他語言的文字,所以就挑選了常用的128個字符,形成了ASCII碼表。其中有一些特殊的字符是不能顯示的,例如換行、空、水平制表符等。如下圖所示:

ascii碼表

? ? ? ?上圖是我打印的ascii編碼,其中索引32的字符為空格,之前的稱為控制字符。但是我們使用isprint函數來判斷一個字符是否“可打印的”,就會發現水平制表符(\t)其實是返回的非0。? ? ? ?

???????? 前輩程序員使用1個字節表示字符,8位二進制一共可表示256個不同的值,但這里只用到了前面的128個位置。在C++里面char是帶有符號的,也就是可以是負數,范圍為[-128, 127],這樣看起來只用[0, 127] ,似乎有點道理(才怪)。

二、擴展ascii編碼

? ? ? ? 128個值當然很快就會不夠用了,IBM于是制訂了擴展ascii編碼,也就是用上另外的128個值。當然這并非標準,到現在已經很少有人使用了,或許在嵌入式單片機等內存緊張的地方還有人使用。

三、多字節編碼(multi bytes)

? ? ? ?如果要包含中文、日文、韓文、俄文、希臘字母、阿拉伯文和數學符號等字符,明顯256個值是不夠的(65536個值也夠嗆),并且為了兼容ascii碼表(已有的定義不能修改)。所以又有聰明人發明了雙字節編碼

????????多字節編碼大多情況都指雙字節編碼,但是這個名字并不準確,因為后面所講的utf-8編碼按原理來說也是多字節編碼,但人們說多字節字符串的時候,往往是指雙字節編碼的字符串。

? ? ? ? 所謂雙字節編碼,就是用1個或2個char來表示1個字符。當用2個char時,可表示的數量就平方了,也就是65536。但實際并非如此,因為它需要兼容ascii碼表。

????????當程序讀取到1個char時,需要先判斷它是否小于等于127,如果小于,則說明它是ascii碼中的字符,它自己1個就代表了1個字符。如果它的值大于127,則說明它后面的一個char與自己組合起來代表一個字符,所以叫雙字節編碼。

? ? ? ? 首先我在vs里輸入了一段utf-8編碼的字符串(在項目命令行里定義/utf-8,可以使字符串字面量為utf-8編碼),可以看到“a中文b”一共占7個char。

utf-8編碼

? ? ? ? 隨后我轉為了雙字節編碼,可以看到變成了占用6個char。因為每個漢字占2個char,并且它的第一個char一定為大于127(char類型溢出到負數),如下圖所示:

雙字節編碼

? ? ? ? ?可以看到,這時編譯器可以正確識別字符串內的內容。因為它默認string的內容編碼是雙字節編碼。如果要在斷點中正確顯示utf-8編碼,可以使用u8string類型,它基于C++20的char8_t類型。不過到目前為止,需要多次點開才能看到字符串的實際內容。

? ? ? ? 回到雙字節編碼,第一個char可表示范圍數量是128,而第二個是256,所以雙字節編碼所能表示的字符數量為128 + 128 * 256,為32896

????????雙字節編碼也可稱為本地編碼,它有各種不同的規格,比如內地用的gb2312標準,臺灣用的big5(大五碼)標準。Windows系統在不同的國家和地區便是使用的不同的本地編碼,使用記事本和excel導出的文件默認情況下都是雙字節編碼,當使用另一臺非相同本地編碼的計算機打開文件時就會出現亂碼。使用notepad++可以看到有如下許多不同的本地編碼:

各式不同的雙字節編碼

? ? ? ? ?在vs里面,通過高級保存選項也可以設定代碼文件使用的編碼,此處并不影響編譯,只是設定文件保存時存儲數據的方式。可以看到每個編碼都擁有代碼頁的定義值。其中中文(gb2312)是936,utf-8是65001(它不屬于本地編碼,但也有代碼頁),還有許多其他國家地區使用的本地編碼。

vs里的代碼頁

? ? ? ? ?通過以上可以清楚的看到雙字節編碼的缺點,依賴它所編成的程序在換一個環境時就會出現亂碼。如果通過指定代碼頁來保證正確,那么這件事也會變得維護艱難。

? ? ? ? 除此之外它還有一個嚴重的缺點,就是漢字被分為2個char,按語義來說“中文”二字的長度為2,但是實際它占4個char,在使用strlen之類的函數時,其含義發生了變化。例如在編寫輸入框控件時,就需要額外的判斷來防止漢字被拆開。當然utf-8編碼也有此缺點。

四、寬字符編碼(wide char)

? ? ? ? 意識到以上問題后,為何不干脆直接用2個字節的類型表示字符呢?一共可表示65536個字符呢。于是出現了寬字符編碼,也就是char變為了wchar_t。但是wchar_t的大小卻沒有規定,在windows系列編譯器里它是2個字節,但在linux系列編譯器里它是4個字節。這個缺點導致代碼在跨平臺時需要額外處理。

? ? ? ? windows平臺為了維護自己的正義性,一般稱寬字符編碼為unicode編碼。這個說法沒有錯誤,但是不夠嚴格。

? ? ? ? 在vs編譯器里面,我們可以設置項目的字符集。其中“使用unicode字符集”指的便是基于wchar_t類型的寬字符編碼。

vs設置字符集

? ? ? ? ?微軟將大部分的函數都寫了兩個版本,一個為A,一個為W。其中這里對應著char和wchar_t。也對應著雙字節編碼和寬字符編碼。也可以叫多字節編碼和unicode編碼。在標準庫中對應的是string和wstring。針對char的函數大部分都又以wchar_t版本實現了一遍,例如wcscmpwcslen

兼容雙字節的寬字符寫法

? ? ? ? ?在代碼中我們可以這樣定義一個寬字符字符串:

寬字符編碼

? ? ? ? ?就目前來說,許多舊工程使用的是雙字節編碼,部分新工程使用的是寬字符編碼,然而還有少許人使用utf-8編碼。先下一個結論,使用utf-8編碼是時代潮流。

? ? ? ? 寬字符編碼有個嚴重的缺點就是它沒有定義大小,并且windows平臺的實現是2字節的大小,對于漢語來說,是根本放不下的,比如《康熙字典》就收錄了4萬7千余漢字,而1994年的《中華字海》便超過了9萬字。外國的程序員們在思考這個問題的時候,便出現了不一致的想法。65536絕已囊括大部分的文字,但是又放不下一些比較少用的文字。

五、unicode編碼

? ? ? ? 所謂unicode編碼就是每個字都用同樣大小的類型保存,目前有基于2字節的UCS-2標準,和基于4字節的UCS-4標準。其中UCS-4是兼容UCS-2的,這意味著UCS-2轉為UCS-4后所有高位為0。

? ? ? ? 其中UCS-2對應2字節的wchar_t,UCS-4對應4字節的wchar_t。但目前用得更多的還是UCS-2,但這并不代表它就應該被用,這有歷史因素在里面。個人認為UCS-4優先于UCS-2,因為它們的缺點都是浪費空間,但UCS-4的優點更明顯,它能表示的范圍又平方了,40多億的范圍。

六、utf-8編碼

? ? ? ? ?用4個字節來表示文字,在處理時不會被拆開,一個字就是一個此類型。但是在表示英文和常用符號時卻浪費了3倍的空間。于是聰明的程序員發明了utf-8編碼,用它來壓縮UCS-4編碼,根據字符的值來占據不同的字節個數,所以它也可以視為“多字節編碼”,叫變長字節編碼更合適。

? ? ? ? 它是以char類型作為基礎,這樣在兼容ascii碼時,就不需要進行截斷。首先[0, 127]與ascii編碼相同,當大于127時,它的最高位一定是1。

二進制十進制
01111111127
1000000128
11111111255

? ? ? ? 所以[0, 127]的值對應[00000000, 01111111],這與ascii碼表一致,如果用x來替代可變位,則是0xxxxxxx

? ? ? ? 當最高位為1時,如果我們用1xxxxxxx來表示另一堆字符,那么又可表示128個字符。所以一共可以表示256個字符……

? ? ? ? 很明顯,1個char辦不到更多的表示了,所以我們后面增加1個char。但是我們怎么知道后面的char是和前面一起的呢?所以我們約定最高位為1時,后面額外有個char。即如下兩種情況:

二進制范圍十進制范圍值
00000000 - 01111111128
10000000 00000000 - 11111111 1111111132768

? ? ? ? 可以看到雙字節編碼最多可以表示32896個字符,這與前面的計算結果一致。本身2字節可以表示的范圍值為65536,這就是壓縮數據帶來的范圍損耗。如果我們用x來表示可變位,那么根據x的數量計算2^x,則能更清晰的看到范圍值:

0xxxxxxx 2^7

1xxxxxxx xxxxxxxx 2^15

? ? ? ? 這時我們會發現一個問題,此時無法表示后面擁有更多的char。因為第二高位是x,它的數據是變化的,無法作為控制位。假設我們以2位來表示后面所接的char數量:

00xxxxxx 后面0個char

01xxxxxx 后面1個char

10xxxxxx 后面2個char

11xxxxxx 后面3個char

? ? ? ? 很明顯,01xxxxxx會不兼容ascii編碼,所以我們總結出:“在有后接char時,最高位必須為1”。所以我們再次修改定義:

0xxxxxxx 后面0個char

100xxxxx?后面1個char

101xxxxx?后面2個char

110xxxxx?后面3個char

111xxxxx?后面4個char

? ? ? ? 根據x的數量,我們得出各自的表示范圍值:

首字節二進制x數量
0xxxxxxx72^7128
100xxxxx5+82^138192
101xxxxx5+162^212097152
110xxxxx5+242^29...
111xxxxx5+322^37...

? ? ? ? 通過這個方法,3個char可表示完UCS-2,而5個char才能表示完UCS-4。當字符的unicode編碼小于128時,只用1個char。小于8192+128時使用2個char。在約200萬的范圍內只需要3個char。但要完全涵蓋UCS-4,則必須是5個char。

? ? ? ? 但以上只是我們實驗的方法,是數值范圍最大的表示情況,實際上utf-8擁有糾錯碼,所有的后繼char都以10開頭(10xxxxxx)。它建立在一個斷言之上:“字符的首個char必定不是10開頭”。如下所示:

首字節二進制x數量
0xxxxxxx72^7128
110xxxxx 10xxxxxx5+62^112048
1110xxxx?10xxxxxx(x2)4+122^1665536
11110xxx?10xxxxxx(x3)3+182^212097152
111110xx?10xxxxxx(x4)2+242^26...
1111110x?10xxxxxx(x5)1+322^33

? ? ? ? ?所以utf-8編碼用2個char可以表示2048+128個值,3個char能夠表示完UCS-2。要表示完整的UCS-4需要6個char。至于有些文章說utf-8編碼的變長范圍為[1, 4]是不正確的,由于4個char能表示約200萬的字符,在當前看來是正確的。

? ? ? ? 在實際使用時,我們還會發現utf-8編碼有帶BOM和不帶BOM的。所謂帶BOM就是在文件頭部多寫入幾個字節來表示自己是utf-8文件,并且可以標明自身的字節序。有標準推薦使用帶BOM的utf-8,而微軟也是這么操作的。但是我認為BOM屬于文件的元數據(meta),一堆數據在那兒,它是什么取決于程序怎么解讀,而一堆數據的一部分內容描述自己是什么,在一定程度上是多此一舉。

七、結語

? ? ? ? 個人認為項目應該統一使用utf-8無BOM編碼,在做文本編輯等邏輯時轉換到utf-32編碼,以進行字符串拆分、排版和顯示等操作。

? ? ? ? 雖然C++20提出了char8_t類型來專門表示utf-8編碼,但我們還是可以用char數組和string來表示utf-8字符串,因為編譯器依舊支持字符串字面量直接為utf-8編碼。在使用std::filesystem時,需要強轉指針為u8string,這樣標準庫才知道你傳入的是utf-8字符串,如下所示:

?????????

? ? ? ? 如果你覺得此文章寫得不錯,可以點擊收藏,然后點擊關注,這可以極大的支持我發更多的文章。

? ? ? ? 你還可以加我的QQ群討論:游戲編程星云閣 170100866

總結

以上是生活随笔為你收集整理的字符编码简介的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。