Unicode、ANSI、UTF-8、GBK详谈

最近在写网络通信上的一些东西,快被这些编码格式搞崩溃了。

 

一、什么是编码

  编码是对现有“符号”进行转化,可以存储在计算机中,在没有计算机时,我们的使用的“符号”,都是手写的,我们的大脑对其编码,这样我们就能记住和识别。但计算机只能存储电信号,即二进制。所以,我们需要对其编码,能使计算机储存。

  各个国家和地区所制定了不同 ANSI 编码标准中,都只规定了各自语言所需的“字符”。这样就不利于交流,所以就有了Unicode编码。

  名词解析:

  1.ANSI:众所周知,刚开始计算机是在美国出现的,所以他们也是第一批考虑编码的,第一开始由于只是用于计算,所有,只用了ASCII码,但后来计算机飞速发展,已经不仅仅用于计算。所以美国就提出了ASNI标准来进行编码。后来,中日韩等又提出了自己的编码编码,例如中国的GBK,但还是统称为ANSI标准。因此它是一个编码集,并不是特定指一种编码格式。

  2.Unicode:这个又称万国码,设计的初衷就是设计出一种通用的编码集。

  3.UTF-8:这是Unicode的一种实现方式。Unicode设计初衷是好的,但是由于是万国码,所以要用4个字节来进行编码,但是,如果使用Unicode编码,存储英文字符和数字时,就会浪费存储空间,因此需要使用一种能减少存储空间的方法。UTF-8就应运而出。

 

二、utf-8具体实现。

Unicode符号范围      | UTF-8编码方式

(十六进制)                 | (二进制)

----------------------+---------------------------------------------

0000 0000-0000 007F | 0xxxxxxx

0000 0080-0000 07FF | 110xxxxx 10xxxxxx

0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx

0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

其中x填入的即是对应的Unicode编码。

 

UCS-2和UCS-4

Unicode是为整合全世界的所有语言文字而诞生的。任何文字在Unicode中都对应一个值,这个值称为代码点(code point)。代码点的值通常写成 U+ABCD 的格式。而文字和代码点之间的对应关系就是UCS-2(Universal Character Set coded in 2 octets)。顾名思义,UCS-2是用两个字节来表示代码点,其取值范围为 U+0000~U+FFFF。

为了能表示更多的文字,人们又提出了UCS-4,即用四个字节表示代码点。它的范围为 U+00000000~U+7FFFFFFF,其中 U+00000000~U+0000FFFF和UCS-2是一样的。

要注意,UCS-2和UCS-4只规定了代码点和文字之间的对应关系,并没有规定代码点在计算机中如何存储。规定存储方式的称为UTF(Unicode Transformation Format),其中应用较多的就是UTF-16和UTF-8了。

 

三、怎么对编码进行转换。

  首先要明确一个概念,由于网络中大部分都是利用utf-8进行编码的(这是因为utf-8通用,且同样的内容所占字节更少)。我们所谓的转换编码,大部分是转换到utf-8利于传输,但utf-8仍然属于Unicode编码,所以就会有这样一个逻辑关系。

  ANSI(GBK)需先转换到 Unicode,从Unicode在转换为utf-8,反之亦然。

  首先是ANSI转换为Unicode:这一步,是我刚开始最不理解的。因为底层编码都是不同。利用汉字“严”。其Unicode编码为 4E25,但其GBK 编码为:D1CF。查了一点资料,发现和自己想的一样,是利用对应的编码表转换。

  其次是Unicode转换为UTF-8。这一步,了解到具体的UTF-8的构成,我们就可以很容易搞懂其具体原理。

 

四、我所找到的函数

  声明:以下函数均摘自别人博客。

  1.UTF-8和GBK互转。

    这与我前面所述,并无冲突。只是隐含了转到Unicode这一过程。这是利用 WindowsAPI来实现的。

    传送门:

      https://blog.csdn.net/xiaohu_2012/article/details/14454299

    

#include<windows.h>
#include<string>
std::string UTF8ToGBK(const char* strUTF8)

{

    int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, NULL, 0);

    wchar_t* wszGBK = new wchar_t[len + 1];

    memset(wszGBK, 0, len * 2 + 2);

    MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, wszGBK, len);

    len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);

    char* szGBK = new char[len + 1];

    memset(szGBK, 0, len + 1);

    WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);

    std::string strTemp(szGBK);

    if (wszGBK) delete[] wszGBK;

    if (szGBK) delete[] szGBK;

    return strTemp;

}
std::string GBKToUTF8(const char* strGBK)

{

    int len = MultiByteToWideChar(CP_ACP, 0, strGBK, -1, NULL, 0);

    wchar_t* wstr = new wchar_t[len + 1];

    memset(wstr, 0, len + 1);

    MultiByteToWideChar(CP_ACP, 0, strGBK, -1, wstr, len);

    len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);

    char* str = new char[len + 1];

    memset(str, 0, len + 1);

    WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);

    std::string strTemp = str;

    if (wstr) delete[] wstr;

    if (str) delete[] str;

    return strTemp;

}

    这里利用的是Windows API:

            MultiByteToWideChar()
            WideCharToMultiByte()
    需要说明的vs编辑问题。https://blog.csdn.net/a3192048/article/details/82154194 

    工程属性里,字符集可以选择“使用Unicode字符集”和“使用多字节字符集”。此选项只控制代码里的API是用宽字符版(即Unicode)的还是ANSI字符版(即GBK)的,它控制不了代码里的字符是用Unicode编码还是ANSI编码。


    如果选择了“使用Unicode字符集”,则代码里用到的API被解释为Unicode版本的API(带标记W的API),如MessageBox被解释为MessageBoxW;
    如果选择了“使用多字节字符集”,则代码里用到的API被解释为ANSI编码版本的API(带标记A的API),如MessageBox被解释为MessageBoxA。

    这里真是纠结半天,其实我们编写的代码页仍然是系统默认的编码集,Windows一般是gbk。还有你在vs中设置保存为 utf-8时,会发现你不能在程序中,使用汉字字符串。这也是vs最坑爹的地方。

  2.UCS-2转utf-8。这就是Unicode转utf-8。

  传送门:https://blog.csdn.net/go_to_learn/article/details/8048472

五、我的感悟

  写了一个传输utf-8的简单通信,这个编码就能困扰我两天。c++对这些底层编码真是没有现成的库。不像Java那样封装好了。Java中一行代码就可以搞定的事,我自己折腾了好久。不过也真正懂得了编码的概念。以前经常混淆。

  碰壁之处:

    我的传输数据中有汉字,需要转码成utf-8进行传输,否则服务器会识别不出来我传输的数据。由于字符和英文属于127的字符。Unicode和GBK都是相同的。不存在转码问题。只有汉字需要转码。

  解决方法:

    在封装数据包时,将汉字转码为utf-8格式。由于 特殊字符和英文在utf-8和gbk中都相同。所以我们封装的数据包就相当于utf-8的数据包,虽然我们只转码了汉字。这样服务器就可以正常解析了。

 

 
posted @ 2018-12-08 21:22  昔时  阅读(1494)  评论(0编辑  收藏  举报