字元編碼再次剖析

鴨脖發表於2012-04-22

這幾天一直在研究字元編碼,也學到了不少的知識,現在在此進行細細總結。轉載請註明出處。

首先引用一段網上對字元編碼的風趣解釋:


問:計算機是怎麼表示文字的? 他不會. 他就會說0和1.
於是乎 有一幫人為了表示字母和各種符號,就發明了ASCII 實際上就是固定了.一個八位的位元組固定的01排列來表示固定的字元.
比如.字母A 的ASCII碼就是.65. 這個排列就是.01000001.當然,這只是ASCII的編碼,同樣呢,有好多好多種編碼,當然道理是一樣的. 當你要把這個串"A",傳送到別的機器,千萬要記住,你發的不是文字"A",你發過去的是一串01000001(ASCII的時候).


那麼現在我們就要好好理解一下字元和位元組的概念了,一個字元可以由任意整數個位元組進行表示,這也就是說,一個字元可以由一個位元組表示,也可以由多個位元組表示,如在ASCII中一個字元由一個位元組組成,而gb2312和ASCII相容,而且由兩個位元組來表示中文,gbk是gb2312的超集,這也就意味著gb類的字元編碼是變長儲存的。而unicode(預設的實現方式是小頭儲存,即ucs-2)每個字元是由兩個位元組儲存的,且字元的第二個位元組在儲存的時候放在前面,而且通過補0和ascii形成假相容,unicode big endian即所謂的大頭儲存,即字元的第一個位元組放在前面。這兩種unicode的儲存方式造成了儲存空間的浪費,而且與ascii是假相容的。而utf-8解決了上述的兩個問題,目前windows下的預設編碼方式是ansi,即gbk,在vc6.0下是ansi,而在vs2010已經採用了utf-8的編碼方式。

在這裡我們可以使用ultraedit的十六進位制檢視工具來檢視不同編碼的檔案的儲存結構。分別將一個有相同的文字內容的文字檔案另存為ansi編碼,unicode編碼,unicode big endian編碼和utf-8編碼的檔案並且用ultraedit開啟,切換至十六進位制檢視下,進行檢視。可以看到,ansi檔案沒有字首,ascii字元用一個位元組,而中文用兩個位元組。而unicode編碼的檔案有字首FF FE,unicode big endian有檔案字首FE FF,而且可以看到不管任何字元都用兩個位元組表示,其中有很多的00位元組,這勢必會在c語言中字串的操作上帶來很大的不便。utf-8有檔案字首EF BB BF,而且中文是用三個位元組表示的。可以通過檢視vc和vs生成的檔案來檢視其不同的編碼方式。


現在我有一箇中英文混合的utf-8編碼的檔案,想逐行列印其中的每一個字元,那麼可以使用下面的程式:

string path = "C:\\Users\\Yelbosh\\Desktop\\tem.txt";
            System.IO.StreamReader sr = new System.IO.StreamReader(path, Encoding.UTF8);
            string tempstr = sr.ReadToEnd();
            foreach (char tempc in tempstr)
            {
                Console.WriteLine(tempc.ToString());
            }
這裡其他編碼方式的檔案當然也可以,我們最主要使用的是foreach函式,這裡可以通過streamreader按照不同的編碼方式解析檔案的內容。


C#也提供了很多的方法來進行不同編碼方式之間的位元組陣列的轉換。使用encoding的convert方法可以在不同的編碼方式之間進行轉換。

 string str = "你好嗎我其實h好的i";
            System.Text.Encoding gb2 = System.Text.Encoding.GetEncoding("GB2312");
            byte[] b1 = gb2.GetBytes(str);
            /*
            foreach (byte b in b1)
            {
                Console.Write(b.ToString("X2")); Console.Write("\t");
            }
            
            Console.Write("\n");
             *  * */
            System.Text.Encoding uni = System.Text.Encoding.GetEncoding("Unicode");
            string msg = uni.GetString(System.Text.Encoding.Convert(gb2, uni, b1));
            Console.Write(msg);

通過實驗我們可以得出正確的結論。其實我們也可以看出字串的顯示已經脫離了底層,已經不屬於編碼方式的範疇了。


但是c#並沒有給出原始碼,那麼計算機如何進行不同編碼方式之間的轉換呢?它是怎麼識別變長儲存呢?如何用c++編寫不同字元編碼方式之間的轉換呢?請繼續關注該部落格~

相關文章