關於字元編碼的問題

2021-05-22 09:49:08 字數 2892 閱讀 7078

寫程式的人基本上都會遇到亂碼的問題,之前自己對字符集、編碼等問題也是一知半解,大概明白什麼意思,但卻說不清楚。由於公司需要做多語言,於是研究了一下,終於把字符集和編碼等問題弄明白了。

ascii、gb2312、gbk、unicode、utf-8、utf-16、ucs2、ucs4......,對於很多人來說這些東西都是比較模糊的(以前的我也是),字符集編碼問題不理解透徹,很難說清楚他們之間的關係。下面就從頭開始把這些概念整理一下,希望對大家有幫助,自己也總結一下。

計算機只認識0和1,因此世界上的任何符號在計算機中都必須轉換成0和1來表示,所謂字符集就是乙個字元對應到數字編碼的對應表。於是最先有了ascii碼,它是用乙個位元組(8位)來表示字元。ascii的第乙個bit永遠是0,因此ascii碼最多能表示128個字元(2的7次方)。英語大小寫字母共52個字母,加上數字和一些控制符號(如回車、tab等),128也夠用了。

但隨著計算機的普及,除了英語以外的其他語言(如中文)使用ascii碼就不行了。於是每個國家都為自己的語言定義了一套字符集,以中文為例,有了gb2312、big5等字符集。gb2312中收錄了7000多個常用的簡體中文本元,而big5為台灣用的正體中文。gb2312和big5等字符集都是用兩個位元組來儲存的,因為乙個位元組只能表示128個字元。新的字符集出來,程式問題也相應的出來了,以前的程式處理字元都是1個1個位元組的處理字元,而新的字符集要求兩個兩個的處理字元,那我們的程式到底是該乙個乙個位元組讀取還是兩個兩個位元組的讀取呢?很快人們發現ascii碼都是以0開頭,那麼新的字符集都用1開頭問題就解決了。程式讀到以0開頭的位元組就乙個位元組乙個位元組的讀字元,遇到1開頭的位元組就兩個兩個位元組的開始讀。因此gb2312、big5等字符集和ascii是相容的。

gb2312只收錄了7000多個字元,並沒有收錄所有的中文字元,因此在2023年和2023年,我國先後推出了gbk1.0和gb18030。gb18030收錄了所有的中文字元,包括少數民族的文字。到此我們有了乙個中文字符集的發展線路:ascii -> gb2312 -> gbk1.0 -> gb18030 他們是由小到大的,並且是向下相容的。到此中文的問題解決了,似乎一切都ok了。

但世界上如此多的國家,每個國家都有一套自己的字符集,這樣太亂了,於是老大哥iso開始推出統一全世界字元的字符集了——unicode(也稱ucs)。unicode占用4個位元組,總共可以收錄2147483648個字元,這足以涵蓋地球上所有用到的字元了。但乙個字元4個位元組相當的浪費資源,特別是在網路傳輸時。於是unicode推出了2個標準,分別是ucs-2和ucs-4。

usc-2用2個位元組儲存字元,其包含了西歐和亞洲絕大多數國家的字元,常用unicode採用usc-2。usc-4用4個位元組儲存字元,這種基本上很少用到,因為太浪費資源。

utf-8、utf-16是unicode的編碼格式,這裡需要搞清楚字符集和編碼格式的區別。字符集是乙個字元和數字的對應表,表示每乙個字元對應的數字,而編碼是指這些字元對應的數字在計算機中如何儲存。比如,字元「中」對應的unicode碼為4e2d,但在計算機中儲存時不一定就是4e2d。(注意,此處寫的是不一定)

先說簡單的utf-16,utf-16用固定兩個位元組對unicode進行編碼,因此utf-16編碼就等於unicode碼。例如,字元「中」對應utf-16編碼為4e2d。這中間又必須考慮位元組序的問題,因為不同的平台對於位元組序的處理方式不一樣,有的是高位在前低位在後,而有的正好相反。因此,字元「中」的utf-16有兩種編碼方式,分別是4e2d和2d4e。那程式如何知道是哪種呢?於是有了bom( bill of material),簡單點說就是在檔案最前面加乙個標記(佔2個位元組,其實也是乙個unicode字元)來表示高位在前還是低位在前。如果檔案最前面是 feff則表示高位在前,又叫big-endian,如果是fffe則表示低位在前,又叫little-endian。

utf-8比較麻煩一點,他是編碼是變長的,也就是說不是使用固定的兩個位元組來進行編碼。對於0-127的字元採用0******x的形式儲存(1個位元組),128-2047採用110***xx 10******的形式儲存(兩個位元組),2048-65535採用1110***x 10****** 10******(三個位元組)的形式儲存。舉例來說,字元「中」對應的unicode碼為4e2d(0100111000101101)也就是20013,在2048-65535之間,因此「中」的utf-8編碼為1110

0100

10111000

10101101。對於utf-8編碼來說,程式讀到0開頭的位元組表示只需要讀乙個位元組,遇到110開頭的表示需要讀取兩個位元組,而讀到1110開頭的表示要讀取三個位元組。因此對於有大量英文本元的文件而言,使用utf-8編碼可以節約大量磁碟空間。utf-8是不需要bom的,因為它是單個位元組處理的,但也可以為utf-8檔案加上bom。為utf-8加上bom後,在檔案頭會多出3個位元組,為 ef bb bf,它就是feff對應的utf-8編碼。

ok,總結一下吧,ascii、gb2312、gbk、big5、unicode都稱為字符集,而utf-8、utf-16叫做編碼方式(其實gb2312也有其編碼方式,此文暫不討論)。一般情況下,盡量使用utf-8編碼方式,因為它即通用又能很好的節約空間。

我們可以做一些實驗檢驗一下上文所述內容,下面是我檢驗的結果,大家可以自己對照做一下看看結果是否一樣。

文字內容

文字格式

占用空間大小

aascii

1位元組a

unicode

4位元組 (fffe + a字母)

aunicode big endian

4位元組 (feff + a字母)

autf-8

1位元組a

utf-8+

4位元組 (efbbbf + a字母)

中ascii

2位元組中

unicode

4位元組中

unicode big endian

4位元組中

utf-8

3位元組中

utf-8+

6位元組

關於字元編碼的問題

這幾天一直在改 前輩 流下來的乙個程式,其中讓我很無語的是 他的字元編碼居然全在servlet 中進行轉碼的,就是這樣 string submit new string request.getparameter submit1 getbytes iso 8859 1 gbk 居然寫成這樣了 讓我來擴...

關於字元編碼的問題

在乙個activity裡面有乙個文字輸入框,我在後台通過 edittext et username edittext findviewbyid r.id.username string username new string et username.gettext tostring 這樣拿到的字串不...

關於字元編碼的問題。

字串它是乙個資料型別,除此之外,它還是乙個編碼的問題。因為計算機它只能處理數字,要想處理文字,就必須得把文字轉換成數字,這樣計算機才可以正常處理。大家都知道ascii碼,這個是外國人發明的,所以最早只有127的個字元被編碼,如數字 字母以及一些特殊符號啥的。如果要處理中文,乙個位元組肯定是不夠的,至...