字符编码 编码转换 乱码

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.GBKGB2312的关系

字符编码 编码转换 乱码_第1张图片


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 

[ { "GUID": "", "OID": -1, "ID": 4002, "UserID": null, "Name": "鏉庢瘏", "Sex": null, "NationCode": null, "Birthday": null, "IdentityCardID": null, "AreaCode": null, "HomeAddress": null, "WorkAddress": null, "TelephoneNum": null, "MobilePhoneNum": null, "NameSpell": null, "BabyName": "鏉庢檽鑹? } ]

问题来了,中文乱码了。。。

 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.windowslinux收发汉字乱码

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_ACPANSI


你可能感兴趣的:(字符编码 编码转换 乱码)