轉換編碼

1.UTF-8 轉換 Unicode

   在編寫FTP Client時,發現通過recv獲取的數據是採用UTF-8方式進行編碼的,直接用Unicode方式進行顯示時會發生錯誤。採用MultiByteToWideChar也無法正確轉換(default是Ascii to Unicode。是我的設置問題?沒有仔細研究)。
    因此學習了下UTF-8的編碼原理,參考如下:
標準的UTF-8是有一個頭(是EF BB BF)和Unicode有一個(FF FE頭一樣),每一個字可以由一個byte(如:英文字母、數字),也可以由二個byte(如:泛歐語系、斯拉夫語字母),也可以由三個byte組成(如:漢字)。一般由四個byte組成的字很少很少。UTF-8編碼模板如下:
UCS-4 range (hex.) UTF-8 octet sequence (binary)
0000 0000-0000 007F 0xxxxxxx
0000 0080-0000 07FF 110xxxxx 10xxxxxx
0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

從這個頭之後的第一個byte,如果是0xxxxxxx形式,就說明這個字只有一個byte組成,如果是110xxxxx形式就說明這個字有二個byte組成,如果是1110xxxx形式就說明有三個byte組成,如果是11110xxx就說明有四個byte組成。

轉換算法可以簡單的理解爲:
    首先判斷這個UTF-8有沒有頭(EF BB BF),沒有從第一個byte開始處理,有頭就跳三個byte,讀到真正的內容。沒有的話,第一個byte就是真正的內容。再把讀到的第一個byte分析成(0000 0000---1111 1111)和UTF-8編碼格式相比較,得出這個真正的字有幾個位。如果是隻有一個byte(格式爲0xxxxxxx),直接就把這個字符以寬字符寫進文件就行了。如果是二byte(格式爲110xxxxx),我們就要算出xxxxx處的值,再讀一個byte得到10xxxxxx也同樣要算出xxxxxx,把這兩個連在一起xxxxx和xxxxx組成一個二進制數,得到這個二進數的十進制值,再以寬字符寫進文件就行了。如果是有三位(格式爲1110xxxx),要再讀兩個byte得到10xxxxxx和10xxxxxx,把這xxxx 和xxxxxx和xxxxxx連在一起組成二進制數,把他再轉成十進制,把這個值以寬字符寫進文件。三個字節、四個字節的一次類推。

簡單的轉換code參考Unicode下char*(UTF-8)轉CString(Unicode):


CString CTTXDlg::g_f_pchar_to_wcs(const unsigned char * p )
{
    CString wstr(_T(""));
    wchar_t wc=L'1';
    size_t r=0; //size_t unsigned integer Result of sizeof operator
    while (1)
    {
        if(*p==NULL||*p==char(0))//原文爲*p=='0',有錯,改*p==char(0)
            break;
        r=g_f_u8towc(wc,p);//從UTF-8格式的地址中,讀一個wchar_t出來
        if(r==-1)//出現錯誤
            AfxMessageBox(_T("g_f_u8towc error"));
        p=p+r; //移位,準備下一次的轉換
        wstr+=wc;//給CString 附值
    }
    return wstr;


size_t CTTXDlg::g_f_u8towc(wchar_t &dest_wchar, const unsigned char * src_str)
{
    int count_bytes = 0;
    unsigned char byte_one = 0, byte_other = 0x3f; // 用於位與運算以提取位值 0x3f-->00111111
    wchar_t tmp_wchar = L'0';
    if (!src_str)
        return (size_t)-1;
    for (;;) // 檢測字節序列長度,根據第一個字節頭的1個個數
    {
        if (src_str[0] <= 0x7f){
            count_bytes = 1; // ASCII字符: 0xxxxxxx( ~ 01111111)
            byte_one = 0x7f; // 用於位與運算, 提取有效位值, 下同 01111111
            break;
        }
        if ( (src_str[0] >= 0xc0) && (src_str[0] <= 0xdf) ){
            count_bytes = 2; // 110xxxxx(110 00000 ~ 110 111111)
            byte_one = 0x1f; //00011111 第一字節有效位的個數
            break;
        }
        if ( (src_str[0] >= 0xe0) && (src_str[0] <= 0xef) ){
            count_bytes = 3; // 1110xxxx(1110 0000 ~ 1110 1111)
            byte_one = 0xf; //00001111
            break;
        }
        if ( (src_str[0] >= 0xf0) && (src_str[0] <= 0xf7) ){
            count_bytes = 4; // 11110xxx(11110 000 ~ 11110 111)
            byte_one = 0x7;
            break;
        }
        if ( (src_str[0] >= 0xf8) && (src_str[0] <= 0xfb) ){
            count_bytes = 5; // 111110xx(111110 00 ~ 111110 11)
            byte_one = 0x3;
            break;
        }
        if ( (src_str[0] >= 0xfc) && (src_str[0] <= 0xfd) ){
            count_bytes = 6; // 1111110x(1111110 0 ~ 1111110 1)
            byte_one = 0x1;
            break;
        }
        return (size_t)-1; // 以上皆不滿足則爲非法序列
    }
    // 以下幾行析取UTF-8編碼字符各個字節的有效位值
    //先得到第一個字節的有效位數據
    tmp_wchar = src_str[0] & byte_one;
    for (int i=1; i<count_bytes;i++)//原文這裏不完整,補充了
    {
        tmp_wchar <<= 6; // 左移6位後與後續字節的有效位值"位或"賦值
        tmp_wchar = tmp_wchar | (src_str[i] & byte_other);//先與後或
    }
    // 位值析取__End!
    dest_wchar = tmp_wchar;
    return count_bytes;
}


友情感謝:
http://www.magicwolf.cn/hobby/utf-8-to-unicode.html
http://blog.sina.com.cn/s/blog_4d25c9870100chmu.html

http://blog.sina.com.cn/s/blog_697414470100qkgm.html

Java的class文件採用utf8的編碼方式,Java的字符串是unicode編碼的。

什麼是 Unicode?

 如果把各種文字編碼形容爲各地的方言,那麼Unicode就是世界各國合作開發的一種語言。

 在這種語言環境下,不會再有語言的編碼衝突,在同屏下,可以顯示任何語言的內容,這就是Unicode的最大好處。那麼Unicode是如何編碼的呢?其實非常簡單。

  就是將世界上所有的文字用2個字節統一進行編碼。可能你會問,2個字節最多能夠表示65536個編碼,夠用嗎?

  韓國和日本的大部分漢字都是從中國傳播過去的,字型是完全一樣的。

  比如:“文”字,GBK和SJIS中都是同一個漢字,只是編碼不同而已。

  那樣,像這樣統一編碼,2個字節就已經足夠容納世界上所有的語言的大部分文字了。

  Unicode的學名是"Universal Multiple-Octet Coded Character Set",簡稱爲UCS。

 unicode包含兩個概念,一是字符集,二是編碼方式。utf-8是unicode的其中一種編碼方式。

 UCS 和 Unicode 只是分配整數給字符的編碼表. 現在存在好幾種將一串字符表示爲一串字節的方法. 最顯而易見的兩種方法是將 Unicode 文本存儲爲 2 個 或 4 個字節序列的串. 這兩種方法的正式名稱分別爲 UCS-2 和 UCS-4. 除非另外指定, 否則大多數的字節都是這樣的(Bigendian convention). 將一個 ASCII 或 Latin-1 的文件轉換成 UCS-2 只需簡單地在每個 ASCII 字節前插入 0x00. 如果要轉換成 UCS-4, 則必須在每個 ASCII 字節前插入三個 0x00.

在 Unix 下使用 UCS-2 (或 UCS-4) 會導致非常嚴重的問題. 用這些編碼的字符串會包含一些特殊的字符, 比如 '\0' 或 '/', 它們在 文件名和其他 C 庫函數參數裏都有特別的含義. 另外, 大多數使用 ASCII 文件的 UNIX 下的工具, 如果不進行重大修改是無法讀取 16 位的字符的. 基於這些原因, 在文件名, 文本文件, 環境變量等地方, UCS-2 不適合作爲 Unicode 的外部編碼.

UTF-8 有一下特性:

  • UCS 字符 U+0000 到 U+007F (ASCII) 被編碼爲字節 0x00 到 0x7F (ASCII 兼容). 這意味着只包含 7 位 ASCII 字符的文件在 ASCII 和 UTF-8 兩種編碼方式下是一樣的.
  • 所有 >U+007F 的 UCS 字符被編碼爲一個多個字節的串, 每個字節都有標記位集. 因此, ASCII 字節 (0x00-0x7F) 不可能作爲任何其他字符的一部分.
  • 表示非 ASCII 字符的多字節串的第一個字節總是在 0xC0 到 0xFD 的範圍裏, 並指出這個字符包含多少個字節. 多字節串的其餘字節都在 0x80 到 0xBF 範圍裏. 這使得重新同步非常容易, 並使編碼無國界, 且很少受丟失字節的影響.
  • 可以編入所有可能的 231個 UCS 代碼
  • UTF-8 編碼字符理論上可以最多到 6 個字節長, 然而 16 位 BMP 字符最多隻用到 3 字節長.
  • Bigendian UCS-4 字節串的排列順序是預定的.
  • 字節 0xFE 和 0xFF 在 UTF-8 編碼中從未用到.

Unicode與UTF-8之間的轉換

“嚴”的Unicode碼是4E25,UTF-8編碼是E4B8A5,兩者是不一樣的。它們之間的轉換可以通過程序實現。

在Windows平臺下,有一個最簡單的轉化方法,就是使用內置的記事本小程序Notepad.exe。打開文件後,點擊“文件”菜單中的“另存爲”命令,會跳出一個對話框,在最底部有一個“編碼”的下拉條。

1)ANSI是默認的編碼方式。對於英文文件是ASCII編碼,對於簡體中文文件是GB2312編碼(只針對Windows簡體中文版,如果是繁體中文版會採用Big5碼)。

2)Unicode編碼指的是UCS-2編碼方式,即直接用兩個字節存入字符的Unicode碼。這個選項用的little endian格式。

3)Unicode big endian編碼與上一個選項相對應。我在下一節會解釋little endian和big endian的涵義。

4)UTF-8編碼,也就是上一節談到的編碼方法。

需要在哪些時候注意編碼問題?

 1. 從外部資源讀取數據:

 InputStream ins = new FileInputStream("Test.java");   

InputStreamReader instrd= new InputStreamReader(in, "GB2312");  

//我們採用了GB2312編碼讀取外部數據,通過查看streamReader的encoding可以印證:

assertEquals("GB2312", instrd.getEncoding());  

 

2. 字符串和字節數組的相互轉換:

 

我們通常通過以下代碼把字符串轉換成字節數組:

 "string".getBytes();  等價於  "string".getBytes(Charset.defaultCharset()); 

如何從字節數組創建一個字符串呢:

new String("string".getBytes());  


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章