对比的源程序(cpp) :
// 针对汉字的utf-8 和utf-16对比
#include "stdafx.h"
#include "windows.h"
#include <cstdint>
#include <iostream>
#include <bitset>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
char *pc = "中国";
cout << pc[0] << pc[1] << endl;// 中
wchar_t* lpString = L"hi中国";
wcout.imbue(locale("chs"));
wcout << lpString[3] << endl; // 国
cout << "------------wchar_t to utf-8----------------" << endl;
char uf8[10] = { 0 };
int nLenOfUf8, nReturnlen;
nLenOfUf8 = WideCharToMultiByte(CP_UTF8, 0, lpString, -1, NULL, 0, NULL, NULL);
//Header: Declared in Winnls.h; include Windows.h.
if (!nLenOfUf8)
return false;
nReturnlen = WideCharToMultiByte(CP_UTF8, 0, lpString, -1, uf8, nLenOfUf8, NULL, NULL);
if (!nReturnlen)
{
return false;
}
setlocale(LC_ALL, "");
cout << uf8[0] << uf8[1] << endl; // hi
cout << uf8 << " :" << strlen(uf8) << endl; // hi乱码 8
bitset<16> b(lpString[2]);
cout << "wchar_t: " << b << endl;// wchar_t (对后面的utf8就知道wchar_t就是utf-16)
bitset<8> b1(uf8[2]);//1110 XXXX // 高4位
bitset<8> b2(uf8[3]);//10XX XXXX // 中间6位
bitset<8> b3(uf8[4]);//10XX XXXX // 低6位
cout << "utf8: " << b1 << ' ' << b2 << ' ' << b3 << endl;
cout << "---------wchar_t is utf16----utf8 to utf16-------------------" << endl;
wchar_t ch[10] = { 0 };
bitset<16> b4(((uf8[2] & 0x0f) << 12)//根据规则计算,把'中' '国'由utf8转成utf16
+ ((uf8[3] & 0x3f) << 6) + (uf8[4] & 0x3f) );
cout << "utf-16: " << b4 << endl; // 中
ch[0] = ((uf8[2] & 0x0f) << 12) + ((uf8[3] & 0x3f) << 6) + (uf8[4] & 0x3f);// 中
ch[1] = ((uf8[5] & 0x0f) << 12) + ((uf8[6] & 0x3f) << 6) + (uf8[7] & 0x3f);// 国
wcout << ch << endl;// 中国
cout << "--------char-----------------" << endl;
bitset<8> b5(pc[0]);
bitset<8> b6(pc[1]);
cout << "char : " << b5 << ' ' << b6 << endl;// 中 GBK编码的 '中' 的十六进制 D6D0
return 0;
}
输出结果:
从这里可以看出。在vs2013 中 wchar_t 和utf_16,二进制位一样,占两字节。但是char,英文字母一个字节,汉字两个字节。存储同样汉字和前两者二进制位不一样,不是utf-16, 是GBK编码(在gbk 编码表里面查找验证过)。
linux fedora23 下的测试源码如下:
#include <iostream>
#include <cwchar>
#include <bitset>
#include <cstdio>
#include <uchar.h>
#include <clocale>
using namespace std;
int main()
{
printf("%s\n",setlocale(LC_ALL,NULL));
setlocale(LC_ALL,"");
printf("%s\n",setlocale(LC_ALL,NULL));
char c[]="中国h";
char16_t c16=u'中';
char32_t c32=U'中';
wchar_t wc[]=L"中国h";
cout << "char len: " << sizeof(c) << endl;
cout << "char16len: " << sizeof(c16) << endl;
cout << "wchar len: " << sizeof(wc) << endl;
bitset<8> b0(c[0]);
bitset<8> b1(c[1]);
bitset<8> b2(c[2]);
cout << "char 中: " << b0<<' '<< b1<<' '<< b2<< endl;
bitset<16> b16(c16);
cout << "char16中:" << b16 << endl;
bitset<32> b32(c32);
cout << "char32中:" << b32 << endl;
bitset<32> b3(wc[0]);
cout << "wchar中 :" << b3 << endl;
bitset<8> b4(c[6]);
cout << "char h:" << b4 << endl;
bitset<32> b5(wc[2]);
cout << "wchar h:" << b5 <<endl;
wprintf(L"%ls \n", L"wpr Wide char:");
wcout << L"wcout Wide char" <<endl;
locale loc("zh_CN.UTF-8");
wcout.imbue(loc);
wcout << wc[0] << endl;// 想输出中,失败
return 0;
}
输出结果是:
mingw下进行同样的测试 源代码如下:
#include <iostream>
#include <cstring>
#include <cwchar>
#include <clocale>
#include <bitset>
using namespace std;
int main(){
setlocale(LC_ALL,"zh_CN.UTF-8");
char a[] = u8"hi中国";//貌似不是utf-8
char a2[] = "hi中国";
wchar_t a3[] = { '中', '国' };
cout << "char u8: " << strlen(a) << ' ' << sizeof(a) <<endl;
cout << "char : " << strlen(a2) << ' ' << sizeof(a2) << endl;
cout << "wchar_t: " /*<< strlen(a3)*/ << ' ' << sizeof(a3) << endl;
bitset<8> c0(a2[2]);
bitset<8> c1(a2[3]);
cout <<"char : " << c0 << ' ' << c1 << endl;
bitset<8> b0(a[2]);
bitset<8> b1(a[3]);
cout <<"u8 : " << b0 << ' ' << b1 << endl;
bitset<16> b2(a3[0]);
bitset<16> b3(a3[1]);
cout <<"whar_t: "<< b2 << ' ' << b3 << endl;
char16_t u16 = '中';
cout << "u16 : " << sizeof(u16) << endl;
bitset<16> b4(u16);
cout << "u16 : " << b4 << ' ' << sizeof(L'h') << endl;
cout << "----------mingw--u/L貌似只识别英文------------" << endl;
bitset<16> b5(u'h');
bitset<16> b6(L'h');
cout << "u16: " << b5 << endl;
cout << "wchar:" << b6 << endl;
return 0;
}
输出结果如下:
对于同样的汉字,wchar_t ,char16_t 以及char里面存的二进制都一样,且都同等于vs下的char中汉字(gbk编码)。
编译的时候有警告,也不知道是否因为此原因这三者才一样的,没找到如何消除警告的方法。
中文字符编码之GBK,UTF-16和UTF-8
(gbk, utf8 ,utf16 编程中经常会遇到这三种字符编码形式的相互转换问题,以至于许多第三方的库不明原因的调用失败)。
下面是转载一段话,总结的比较好
简单几句话总结Unicode,UTF-8和UTF-16
概念
先说一说基本的概念,这包括什么是Unicode,什么是UTF-8,什么是UTF-16。
Unicode,UTF-8,UTF-16完整的说明请参考Wiki(Unicode,UTF-8,UTF-16)。用比较简单的话来说就是,Unicode定义了所有可以用来表示字符的数值集合(称之为Code Point)。UTF-8和UTF-16等UTF标准定义了这些数值和字符的映射关系。
UTF-8
优势
UTF-8最大的优势是,没有字节序的概念。所以特别适合用于字符串的网络数据传输,不用考虑大小端问题。
劣势
本地字符串处理过程中,如果使用UTF-8,对于英文字符的处理没有太大的问题。一个char变量表示一个英文字符。但是对于中文等远东字符集来说,就比较坑爹了。char str[]; str[0]并不能完整表示一个汉字。UTF-8编码格式下,一个汉字需要至少3个char才能表示。这对于通过下标来操作字符串的操作来说是非常痛苦的一件事情。
另外,一个汉字需要至少3个char来表示,也让汉字在网络传输上存在劣势,占用太多流量。
UTF-16
优势
UTF-16 LE是windows上默认的Unicode编码方式,使用wchar_t表示。所有wchar_t *类型的字符串(包括硬编码在.h/.cpp里的字符串字面值),VC都自动采用UTF-16的编码(字符串字面值,literal string,存在很多坑。特别是char *类型的字面值,最终内存使用何种编码方式完全取决于当前文件的编码方式。也就是说当前文件如果是GBK编码的,那么文件里char * str = "中午",str指向的内存字符串二进制是使用GBK编码的。如果文件编码是UTF-8,那么内存是使用UTF-8编码。所以为什么一直要强调字符串应该放在资源文件里,而不是硬编码在.h/.cpp文件里!)。
UTF-16另外一个优势就是常用字符都可以使用两个个字节表示,也就是一个wchar_t(这里指Windows平台)。所以,在Windows平台上,特别适合使用wchar_t来作为字符串的存储基类型。一个wchar_t表示一个字符。操作使用非常方便。
劣势
没有统一的表示UTF-16编码的字符类型。C++98/03里对wchar_t的定义是非常宽泛的。这导致在Windows平台上,wchar_t是2字节的;在Unix-like系统上是4字节的。代码移植上,可能会遇到挑战(我没移植过,所以不确定会有什么难度,以及难度有多大)。
即使最新的C++11里已经定义除了char16_t表示UTF-16,MS的VS2013还不支持char16_t。所以目前使用char16_t还不具移植性。
据我了解,UTF-16编码和GBK编码相比,还存在一个排序的劣势。也就是说,如果要按照汉语拼音的字母顺序对汉字进行排序,GBK会得到正确的结果,而UTF-16就不行(暂时我还没这种需求,所以我没验证过,不过好像我马上就要与到这种需求了,到时候我再验证下)。
UTF-16编码字符串的网络传输,要考虑大小端的问题。
UTF-32
优势
这个优势就明显了,所有字符都是4字节,fix-length。一个wchar_t(Unix-like系统上)表示一个字符。
劣势
对于以英文为主的字符串来说,空间消耗大。
面临和上面UTF-16一样的问题。一致性,排序,网络传输。char32_t VS2013还不支持(甚至VS 14 CPT也没打算支持)。
总结
UTF-8最适合用来作为字符串网络传输的编码格式。UTF-16最适合当作本地字符串编码格式。如果定义好了网络传输协议,那么UTF-16也非常合适当作网络字符串传输的编码格式,特别是中文等远东地区字符集。比起UTF-8来说,节省流量。UTF-32没什么特殊癖好或者需求的话,暂时还用不上。