编码转换

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 范围里. 这使得重新同步非常容易, 并使编码无国界, 且很少受丢失字节的影响.
  • 可以编入所有可能的 2 31个 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());  



你可能感兴趣的:(编码)