GB UTF-8 UNICODE 汉字编码表:http://blog.chinaunix.net/uid-25544300-id-3281847.html
1.在使用libjson的过程中遇到的编码问题:
push_back(JSONNode("中文","中文"))存进去的汉字,write()出来全变成了"\u00E6\u009A\u0097\u00E6\u0088\u0098"这种形式。
研究发现 E6 9A 97正是“暗”的UTF-8汉字编码。
“暗”的UNICODE编码是“6697”。std::cout<<"\u6697"<
那么如何使E6 9A 97输出对应汉字呢?
char ch[] = {0xE6, 0x9A, 0x97, 0x00};
std::cout<
2.
iconv各种转码,宽字符、多字符转换,都解决不了问题,最后用strlen一测,结果是一个“\u00E6”是六个字节,说明做了转义处理。需要自己转码。
3.宽字符、多字符转换:
3.1 宽字符转多字符函数:int wcstombs(char *dest, wchar_t *src, int max)
把src宽字符串转换成普通字符串存储到dest,max表示允许转换到dest的最大字符数。
返回值:成功返回成功转换的普通字符数、转换到dest的普通字符数。失败返回-1,错误码在errno中。
确定dest大小:
int n = wcstombs(NULL, src, 0);
char dest[n+1];
memset(dest, 0, n+1);
3.2 多字符转宽字符函数:int mbstowcs(wchar_t *dest, char *src, int max)
把src普通字符串转换成宽字符串存储到dest,max表示允许转换到dest的最大宽字符数。
返回值:成功返回成功转换的宽字符数、转换到dest的宽字符数。失败返回-1,错误码在errno中。
确定dest大小:
int n = mbstowcs(NULL, src, 0);
wchar_t dest[n+1];
memset(dest, 0, n+1);
3.3 汉字转换:
要加setlocale(LC_ALL,"zh_CN.UTF-8")或者setlocale(LC_ALL,"") ,否则error : Invalid or incomplete multibyte or wide character。
3.4 宽字符转普通字符函数模板:
int wcstombs2(wchar_t *wch, std::string& str){
setlocale(LC_ALL, "");
int n = wcstombs(NULL, wch, 0);
if(n == -1){
std::cout<<"wcstombs:NULL error : "<
用法:
wchar_t wch[] = {0x8363, 0x738b, 0x516b, 0x0};
std::string str;
if(wcstombs2(wch, str) == -1){
std::cout<<"transform fail"<
3.5 标准格式、参考实例:
setlocale(LC_ALL,"zh_CN.UTF-8");
wchar_t wch[] = L"九州";
int n = wcstombs(NULL, wch, 0);
if(n ==-1){
std::cout<<"wcstombs error : "<
setlocale(LC_ALL, "zh_CN.UTF-8");
char ch[] = "泥马";
int n = mbstowcs(NULL, ch, 0);
if(n ==-1){
std::cout<<"mbstowcs error : "<
对这两函数有任何不理解就看:http://blog.csdn.net/xiaobai1593/article/details/7063535
4.iconv转码:
4.1 编码转换函数:int iconv(iconv_t cd, char **s, size_t *b, char **c, size_t *d)
按照函数iconv_t iconv_open(const char *tocode, const char *fromcode)中指定的编码转化方式,将源字符串转换到目的字符串中。b是原串长度,d是目的地址空间大小。
#include
char src[] = {0x63, 0x83, 0x53, 0x66, 0x1B, 0x54, 0x00};
char dest[100] = {0};
char *src_temp = src;
char *dest_temp = dest;
size_t src_len = strlen(src);
size_t dest_len = 100;
iconv_t cd = iconv_open("UTF-8","UNICODE");
if(cd < 0){
std::cout<<"iconv open error : "<
4.2 编码转换函数模板:
int iconv2(std::string& str, const char *tocode, const char *fromcode){
int n = str.length();
char src[n+1];
char dest[4*n+1]; //任何两种编码之间转换,源串与转换后的目的串大小不会超过4倍
char *src_temp = src;
char *dest_temp = dest;
size_t src_len = n;
size_t dest_len = 4*n;
memset(src, 0, n+1);
memset(dest, 0, 4*n+1);
memcpy(src, str.c_str(),n);
iconv_t cd = iconv_open(tocode, fromcode);
if(cd < 0){
std::cout<<"iconv_open error : "<
5."\\u8d8a\\u6765\\u8d8a\\u6d41\\u884c\\u3002"
5.1 根据UNICODE与普通字符的适应性(7.3)
8d8a 6765 8d8a 6d41 884c 3002 分别是 “越” “来” “越” “流” “行” “。” 的UNICODE编码,限期将其正确解码:
int main(){
char ch[] = "\\u8d8a\\u6765\\u8d8a\\u6d41\\u884c\\u3002";
std::string str(ch);
if(u_decode(str) == -1){
std::cout<<"u_decode error"<
u_decode函数:
int u_decode(std::string& str){
/*create unicode characters corresponding to characters*/
int index = 0;
int i = 0;
std::string substr;
int n = count(str, "\\u");
char src[2*n+3];
memset(src, 0, 2*n+3);
src[i++] = 0xfe;
src[i++] = 0xff;
while((index = str.find("\\u", index)) != std::string::npos){
index += 2;
substr = str.substr(index, 2);
src[i++] = strtol(substr.c_str(), NULL, 16);
index += 2;
substr = str.substr(index, 2);
src[i++] = strtol(substr.c_str(), NULL, 16);
}
str = src;
/*code from unicode to utf-8*/
int res = iconv2(str, "UTF-8", "UNICODE");
if(res == -1){
std::cout<<"iconv2 error"<
5.2 根据UNICODE与宽字符的适应性(7.6)
int main(){
char ch[] = "\\u8d8a\\u6765\\u8d8a\\u6d41\\u884c\\u3002";
std::string str(ch);
int n = count(str, "\\u");
wchar_t wch[n+1];
wmemset(wch, 0, n+1);
int i = 0;
int index = 0;
std::string substr;
while((index = str.find("\\u", index)) != std::string::npos){
index += 2;
substr = str.substr(index, 4);
wch[i++] = strtol(substr.c_str(), NULL, 16);
}
setlocale(LC_ALL, "");
printf("%S\n", wch); //输出宽字符:“越来越流行。”
std::string outbuf;
if(wcstombs2(outbuf, wch) == -1){
std::cout<<"transform fail"<
6.UTF-8汉字编码:
UTF-8用三个字节表示一个汉字,如 0xE6 0x8B 0x97
6.1
char ch[] = {0xe5, 0xbe, 0x95, 0x00}; //此时ch中存储的就是汉字“徕”
std::cout<
6.2 \uXXXX对应的汉字以UTF-8编码存储(XXXX对应汉字的UNICODE编码):
printf("%s\n","\u5f95"); //输出“徕”
char ch[] = "\u5f95";
for(int i = 0; i < strlen(ch); i++)
printf("%#x\n", ch[i]);
输出:
0xffffffe5
0xffffffbe
0xffffff95 //E5 BE 95对应“徕”的UTF-8编码
7.UNICODE汉字编码:
UNICODE所有字符都用两字节表示,当然一个汉字对应两字节,如5F95
机器默认UTF-8,不能得知UNICODE真正的存储格式。
7.1 以UTF-8存储的汉字,用iconv函数转化成UNICODE编码:
char ch[] = "荣"; //ch[0] = 0xe8,ch[1] = 0x8d, ch[2] = 0xa3, ch[3] = 0x0
char dest[100] = {0};
iconv();
for(int i = 0; i < strlen(dest); i++)
printf("%#x\n", dest[i]);
输出:
0xffffffff
0xfffffffe
0x63
0xffffff83 //8363 是 “荣”的UNICODE编码
7.2 FF FE和FE FF是UTF-16的BOM前缀,用来标示大小端存储:
0x8363 是 “荣”的UNICODE编码
FF FE : 小端存储 ch[0] = 0x63 ch[1] = 0x83
FE FF : 大端存储 ch[0] = 0x83 ch[2] = 0x63
7.3 UNICODE与普通字符的适应性:
char ch1[] = {0x63, 0x83, 0x00};
char ch2[] = {0xff, 0xfe, 0x63, 0x83, 0x00};
char ch3[] = {0xfe, 0xff, 0x83, 0x63, 0x00};
经过从iconv从UNICODE到UTF-8转码后,均可输出汉字“荣”。
7.4 以UTF-8存储的汉字,用iconv函数转化成UTF-16编码,得到的结果与7.1相同。
7.3中的字符串用iconv从UTF-16转到UTF-8,结果均可输出汉字“荣”。
结论:UNICODE和UTF-16均固定使用两个字节表示字符。平常所说的UNICODE编码就是UTF-16的。
7.5 荣 0x8363 王 0x738B 八 0x516B
char ch1[] = {0x63, 0x83, 0x8B, 0x73, 0x6B, 0x51, 0x00};
用iconv从UNICODE或UTF-16转码到UTF-8,均可输出“荣王八”。
7.6 UNICODE与宽字符的适应性:
荣 0x8363 王 0x738B 八 0x516B
wchar_t wc = 0x8363;
setlocale(LC_ALL, "");
printf("%C\n", wc); //输出“荣”
wchar_t wch[] = {0x8363, 0x738b, 0x516b, 0x0};
setlocale(LC_ALL, "");
printf("%S\n", wch); //输出“荣王八”
用wcstombs将wch从宽字符转到普通字符后,可以正常输出。“荣王八”以UTF-8存储在char[]中:
wchar_t wch[] = {0x8363, 0x738b, 0x516b, 0x0};
std::string str;
if(wcstombs2(wch, str) == -1){
std::cout<<"transform fail"<
输出:
荣王八
0xffffffe8
0xffffff8d
0xffffffa3 //“荣” utf-8编码:e8 8d a3
0xffffffe7
0xffffff8e
0xffffff8b //"王" utf-8编码:e7 8e 8d
0xffffffe5
0xffffff85
0xffffffab //"八" utf-8编码:e5 85 ab
8.有符号数:
8.1 二进制数大小
最高位表示正负:
正数最高位为0,绝对值等于去掉符号位后二进制转十进制。
负数最高位为1,绝对值等于去掉符号位后的补码。(计算机是用补码来保存负数的)
计算机中求一个补码表示的负数值:去掉符号位后按位取反最后结果加1就是它的绝对值,或者简单点直接拿掉符号位后从最高位按位取反直到最后一个1,然后二进制转十进制,得到的也是绝对值。
求一个负数的补码:最高位1,然后用次高位到最低位表示出它的绝对值,按位取反加1,或者从次高位按位取反直到最后一个1。
8.2 有符号数扩充即扩展符号位。
8.3 char是有符号数,占八位,最高位表正负。
WINDOWS部分:
4.mysql数据库乱码
前言:
VS2010默认汉字编码GB2312、mysql被设置成UTF8,故通过程序插入数据库中的“崇祯”为乱码。
过程描述:
1.原因一直没有找到,不停更改mysql数据库编码、table编码。
2.程序设置断点,debug下汉字正常显示。故认为乱码出在mysql。
3.后经刘XX提醒,即将插入mysql、正常显示的汉字以十六进制打出,对应编码表
结果:程序中正常显示的汉字编码为GB,结合mysql的UTF8,乱码不足为奇。
解决:
A.存mysql前不管汉字与否一律:首先默认编码转宽字符、最后宽字符转UTF8。
B.取mysql后不管汉字与否一律:首先UTF8转宽字符、最后宽字符转默认编码。
3.GBK和GB2312的关系
1.
用GET从HTTP上请求了数据,用recv函数直接收到了char *buffer中,在VS中查看结果
HTTP/1.1 200 OK Date: Thu, 09 Jan 2014 01:29:15 GMT Server: Microsoft-IIS/6.0 X-Powered-By: ASP.NET X-AspNet-Version: 2.0.50727 Cache-Control: private Expires: Thu, 09 Jan 2014 01:29:15 GMT Content-Type: text/html; charset=utf-8 Content-Length: 387问题来了,中文乱码了。。。
HTTP头信息中的charset=utf-8清楚的告诉下面我发到char *buffer中的字节流是utf8编码的。
解决方法:
int m = MultiByteToWideChar(CP_UTF8,0,buffer,-1,0,0);
WCHAR *temp = new WCHAR[m+1];
temp[m]='\0';
MultiByteToWideChar(CP_UTF8,0,buffer,-1,temp,m);
然后:m=WideCharToMultiByte(CP_ACP,0,temp,-1,0,0,0,0);
char *result = new char[m+1];
result[m]='\0';
WideCharToMultiByte(CP_ACP,0,temp,-1,result,m,0,0);
我的理解是:
HTTP编码中给出了编码格式是UTF8,出现乱码的原因是系统使用的默认编码方式对011010的比特流解码,编码与解码不一致,必然出现乱码。所以第一步首先将buffer按照UTF8解码成UNICODE(windows下默认的貌似),参数CP_UTF8。然后再次将UNICODE转换成系统默认的编码,参数CP_ACP 指示 API 使用当前设置默认的 Windows ANSI 代码页进行转换。
收集的信息如下:
一个孤立的字符串的解释依赖于具体的系统,你如果在linux上开发,utf8的默认显示的是正确的,而如果你转成wchar_t反而会乱码。html/xml通过charset属性指明了对这些字节流的理解方式,所以不同平台上都可以正确显示。在各个系统的底层,可能都要转换成特定的编码方式再显示,比如utf-8的串在Windows上会转成unicode, unicode/gbk等编码方式的在linux下多半会转换成utf8串再显示。
Putf8不是操作系统默认的理解方式,所以出现乱码。
同样的二进制数据在不同的字符集中对应不同的字符,要把二进制数据作为文本显示必须指定字符集
高版本的VS在调试中显示数据时,对于wchar_t*指针指向的内存,按照UNICODE编码来对应字符集,对于char*指针指向的内存,按照ASCII编码和操作系统语言设置来对应字符集。当然这些都是显示方面的功能,对于程序本身执行的功能一点影响都没有。
如果你告诉它这个是utf8串,那它会理解,但你不告诉它它不会把所有可能的字符编码方式逐一试遍。
2.windows向linux收发汉字乱码:
4月15号,VS2010,socket通信。
windows发汉字,linux收的是乱码;linux发汉字,windows收的是乱码。
windows使得是ANSI编码,说到ANSI编码这里我少说两句。ANSI表示英文字符时用一个字节、表示中文用两个字节,在简体中文系统下ANSI代表GB2312编码。而UNICODE万国码不管英文字符还是中文都用两字节表示。
linux下默认UTF8编码。编码不统一自然乱码。
解决方法:
发送前转UTF8:
inline std::string win2unix_decode(const char* buffer)
{
int m = MultiByteToWideChar(CP_ACP,0,buffer,-1,0,0);
WCHAR *temp = new WCHAR[m+1];
temp[m]='\0';
MultiByteToWideChar(CP_ACP,0,buffer,-1,temp,m);
m=WideCharToMultiByte(CP_UTF8,0,temp,-1,0,0,0,0);
char *result = new char[m+1];
result[m]='\0';
WideCharToMultiByte(CP_UTF8,0,temp,-1,result,m,0,0);
std::string str = result;
delete[] temp;
delete[] result;
return str;
}
接收后转ANSI:
inline std::string unix2win_decode(const char* buffer)
{
int m = MultiByteToWideChar(CP_UTF8,0,buffer,-1,0,0);
WCHAR *temp = new WCHAR[m+1];
temp[m]='\0';
MultiByteToWideChar(CP_UTF8,0,buffer,-1,temp,m);
m=WideCharToMultiByte(CP_ACP,0,temp,-1,0,0,0,0);
char *result = new char[m+1];
result[m]='\0';
WideCharToMultiByte(CP_ACP,0,temp,-1,result,m,0,0);
std::string str = result;
delete[] temp;
delete[] result;
return str;
}
CP_ACP即
ANSI。