C语义char*显示中文---ASCII、DBCS、Unicode三种编码---char* CString string区别

来源:互联网 发布:网络走马灯是什么意思 编辑:程序博客网 时间:2024/06/05 14:57
char* p = "this is 中国";printf("%s",p);output:this is 中国疑问:p是指向char型的指针,调用printf的时候,它凭什么知道下一个输出的是this is 这样的字符呢,还是"中国"这样的汉字,在内存中"this is "应该是每个字符占一个字节,汉字肯定得要两个字节,到底printf怎么确认下一个输出到底是输出一个字符呢还是将两个字节输出为一个汉字?
上面是一个网友提出的疑问。下面是回答:
网友1:
这是个编码的问题,你可以在VC的debug模式下看每个变量的值,之后你会发现汉字的第一个字节都是负数。而英文、数字以及一些常用的半角符号的编码都在0~255之间。
网友2:
编译器不知道你输入的是字母还是汉字,它只是原封不动地将p指向的内存中的内容打印到屏幕上去而已。怎样解析这些字符是操作系统的事情。操作系统可以判断出哪是英文哪是汉字,因为二者除了占用的字节不一样多以为,在编码上也有能够识别的标志,可以知道是英文还是其它多字节字符。
我分析:

GBK 采用双字节表示,总体编码范围为 8140-FEFE,首字节在 81-FE 之间,尾字节在 40-FE 之间,剔除 xx7F 一条线。总计 23940 个码位,共收入 21886 个汉字和图形符号,其中汉字(包括部首和构件)21003 个,图形符号 883 个。 这是一个显示中西文混合字符串的参考程序。通过上面的分析可以得出,如果是汉字,则首字节在81-FE 之间,其最高位为1,所以补码小于0,应连续显示两个字节;如果是西文字符,则首字节在00-7F之间,其最高位为0,补码大于等于0,只需显示一个字节。

#include <stdio.h> char *str="汉字里有english也能正确显示"; int main() {  int i = 0;  while (str[i] != '/0') {    if(str[i] < 0) {       printf("%c%c/n", str[i], str[i+1]); //连续显示两个字节,输出一个汉字       i++;    }    else {     printf("%c/n", str[i]);    }    i++;   }  return 0;}
 
 
下面讲一下三种主要的编码的区别:

字符基础知识 -ASCIIDBCSUnicode【即,单多双三种】

【DBCS指Double Byte Char Set】

所有的字符串类型都归结为C风格的字符串。C风格的字符串是一系列字符的组合。所以我会首先讨论字符类型。通常有三种编码方案和三种字符类型。

   第一种编码方案是单字节字符集(single-byte character set),或者叫SBCS。在这种编码方案中,所有的字符都是一个字节长。ASCII是SBCS的一个特例。一个字符0标志着一个SBCS字符串的结束。

   第二种编码方案是多字节字符集(multi-byte character set),或者叫MBCS。 多字节编码方案包含了单字节的字符和多于一个字节的字符。在Windows系统中使用的多字节字符集有两种字符类型,单字节字符和双字节字符。因为Windows中使用的多字节字符集最大的字符是双字节的,所以我们通常用双字节字符集(double-byte character set),或者叫DBCS来代替MBCS。

在双字节编码方案中,某些值被保留起来说明他们是一个双字节字符的一部分。例如,在Shift-JIS编码方案中(通行的日语编码方案),位于0x81-0x9F和0xE0-0xFC之间的字符意味着:这是一个双字节字符,第二个字节是这个字符的一部分。这些值叫头字节(lead byte),它们的值通常大于0x7F。在头字节后面的字节叫尾字节(trail byte)。在双字节编码方案中,尾字节可以是任何一个非零值。就像在单字节编码方案中一样,双字节编码方案也是以字符0结束的。

【DBCS是亚洲的字符集,包含了ANSI(即ANSI是其子集),ANSI也就是ASCII值为0-255之间的字符,当字符为ANSI时,存放于文件中占用的是一个字节.如果是非ANSI的呢,则占用两字节.用VB的ASC函数可以很容易得到一个字符的DBCS值(或是说ANSI值吧)。也有ANSI是ASCII的扩展的说法。总之记住ANSI是DBCS的子集就行了。】

   第三种编码方案是Unicode。Unicode是一种所有字符都是两个字节长的编码方案。Unicode字符有时被叫做宽字符,因为它们比单字节字符更宽(占用更多存储空间)。要注意到Unicode不能被考虑成一种多字节字符编码方案。一个明显的特征是Unicode字符的长度和MBCS的长度不一样。Unicode字符串都是以两个字符0来结束的

   单字节字符是由拉丁字母,音节符号和一些在ASCII标准和DOS系统中定义的图形符号组成的。双字节字符用于东亚和中东语言。Unicode用于COM组件和Windows NT内部。

   你肯定已经很熟悉单字节字符了。当你使用char数据类型的时候,你就在使用单字节字符。你也可以使用char来处理双字节字符(这也是你使用双字节字符所遇到的奇怪现象之一)。Unicode字符通常使用wchar_t类型。Unicode字符和字符串常量通常以字母L打头。例如:

wchar_t wch   =  L'1'                //2字节长0x0031wchar_t* wsz  =  L"Hello"       //12字节长,6个宽字符

 

字符在内存中是如何存储的?

单字节字符在内存中都是一个接一个连续存储的,用一个0来结束字符串。例如,"Bob"是这样存储的

42 6F 62 00

B   o   b  EOS

如果是Unicode版本,L"Bob"是这样存储的

42 00 6F 00 62 00 00 00

  B         o        b       EOS

使用字符0x0000来结束这个字符串

 

双字节字符第一眼看上去很象单字节字符串。但是我们随后会看到在使用函数操作字符串和使用指针偏历字符串的时候还是有所不同。字符串" " ("nihongo")是按照下面的方式存储的。(头字节和尾字节用LB和TB来表示)

 93 FA 96 7B 8C EA 00

 LB TB LB TB LB TB EOS

日       本             EOS

需要注意的是,“ni”并不等于0xFA93。两个值93和FA,以上面的顺序组合在一起,用来编码字符“ni”(在Big-Endian的CPU中,字节存储的方式就是象上图那样)

 

使用字符串处理函数

我们都见过象strcpy()sprintf()atol(),等等这些字符串函数。这些函数只能用于单字节字符串。标准库【std】也提供了用于操作Unicode字符串的函数,比如wcscpy(),swprintf()_wtol()

   微软也在CRT(C运行库)中增加了操作双字节字符串的函数。比如strxxx()的函数都有对应的_mbsxxx()函数,以用来操作双字节字符串。如果你预料到你的程序会处理双字节字符串(比如你的程序会安装在日文,中文,或者其它使用双字节字符串的语言中),那么你就应该始终用_mbsxxx()函数。它们也可以接受单字节字符串(因为双字节字符串能包含单字节字符,所以_mbsxxx()函数可以处理单字节字符串)

我们可以举一个典型的例子来显示不同函数的用途。还是回到Unicode字符串L"Bob"

42 00 6F 00 62 00 00 00

  B         o        b      EOS

   因为X86架构的CPU都是little-endian的。值0x0042在内存中存储方式是42 00 你能发现如果我们把这个字符串传入到strlen()中发生的问题吗?它将会首先看到第一个字节42,然后是00,这意味着“字符串结束”。那么strlen()将会返回1。如果我们把它传入wcslen(),情况会变得更坏,wcslen()将会看见0x6F42,然后是0x0062,然后会一直读取下去直到超过缓冲区大小,直到偶然碰到0000或者引起一个GPF错误。

 
 
 
下面是char* CString string区别
转自:http://blog.sina.com.cn/s/blog_79489160010136vb.html

一.介绍

string和CString均是字符串模板类,string为标准模板类(STL)定义的字符串类,已经纳入C++标准之中;

CString(typedef CStringT<TCHAR, StrTraitMFC<TCHAR>> CString)为Visual C++中最常用的字符串类,继承自CSimpleStringT类,主要应用在MFC和ATL编程中,主要数据类型有char(应用于ANSI),wchar_t(Unicode),TCHAR(ANSI与unicode均可);

char*为C编程中最常用的字符串指针,一般以’\0’为结束标志;

 

二.赋值以及构造

//这里使用了一个console控制台程序作为实验

编译环境:xp32系统 vs2005英文版

#include <iostream>

#include <Windows.h>

#include <atlstr.h> //在非MFC程序下使用CString类必须加的头文件

#include <string>

using namespace std;

 

int main(void)

{

         //赋值和构造

         char *ch = "Hello World!";

        

 CString cstr(ch);     //等价于:CString cstr = "Hello World!"; CString("HelloWorld!");

         string str(ch);       //等价于:string str = "Hello World!";  string str("Hello World!");

         string str(cstr);                           //需设置Use Multi-Byte Character Set属性

         cout<<"char* : "<<ch<<endl; 

         cout<<"string : "<<str<<endl;

         cout<<"string : "<<str.c_str()<<endl;               //c_str()函数:将内容以C_string返回

         wcout<<"CString : "<<cstr.GetString()<<endl;        //wcout输出宽字节

         wcout<<"CString : "<<cstr.GetBuffer()<<endl;

         wcout<<"CString : "<<LPCTSTR(cstr)<<endl;

 

         system("pause");

         return 0;

}

在这里 ch是一个字符串指针,自然就没有构造函数,字节赋值就是了,CString和string都是一个类,有构造函数,可以使用基本的字符串构造,包括char *;

Ps:在Win32控制台(console)程序下使用CString有两种方法,第一种:直接添加头文件#include <atlstr.h>,第二种:在solution标签页,右键工程名,选择properties,

弹出text property pages界面,如下图,选择General,在Use of MFC的后面选择 use MFC in a Shared DLL.如下图

如果你使用的是VC++6.0,你可以在菜单栏的 工程->设置中找到此选项。

做完这些后,你还要在程序的前面添加两个头文件,#include <afxwin.h> #include <windows.h>。注意:你必须让#include <afxwin.h> 在#include <windows.h>的前面,这样你就能在程序中使用CString这个类了。

是时候输出来比较下了,在vs默认情况下,使用的是unicode,它在计算机中无论什么时候都是16位的,char *和string都可以用cout直接的输出,但是直接cout CString会出错,你可以在 text property pages界面Character Set 属性中选择Use Multi-Byte Character Set(如果你选择了此选项,你可以使用CString类直接够着string)。或者你也可以使用wcout,实例中使用了这个方法。

3.运算符重载

(1):operator=

我们还是以实例来说明

#include <iostream>

#include <atlstr.h> //在非MFC程序下使用CString类必须加的头文件

#include <string>

using namespace std;

 

int main(void)

{

 

    char *ch = "Hello World!";

    CString cstr = ch; 【估计CString类有一个接收char*的参数的构造函数,自己进行编码转换】                

    string str = cstr;   //同样要设置Use Multi-Byte Character Set属性     

    cout<<"char* : "<<ch<<endl;

    cout<<"string : "<<str<<endl;

    cout<<"string : "<<str.c_str()<<endl;//c_str()函数:返回cstring

    wcout<<"CString : "<<cstr.GetString()<<endl;  //wcout输出宽字节

    wcout<<"CString : "<<cstr.GetBuffer()<<endl;

    wcout<<"CString : "<<LPCTSTR(cstr)<<endl;

    system("pause");

    return 0;

}

可以看出,最方便的是string ,可以用CString和char*直接赋值;

其次是CString,可以用char*赋值;

Char*只是一个指针赋值。

(2)operator+

我们再来看看重载的+

#include <iostream>

#include <atlstr.h> //在非MFC程序下使用CString类必须加的头文件

#include <string>

using namespace std;

 

int main(void)

{

 

    char *ch = "Hello World!";

    CString cstr = ch;

    string str = cstr;

    string str2 = str+ch;    //string + char *

    CString cstr2 = cstr + ch;//CString + char *

    char *ch2 = new char[20];

    ZeroMemory(ch2,20);

    strcat(ch2,ch);

 

    cout<<"char* : "<<ch<<endl;

    cout<<"char2* : "<<ch2<<endl;  

    cout<<"string : "<<str<<endl;

    cout<<"string2 : "<<str2<<endl;

 

    wcout<<"CString : "<<cstr.GetString()<<endl;  //wcout输出宽字节

    wcout<<"CString2 : "<<cstr2.GetString()<<endl;//wcout输出宽字节

    system("pause");

    return 0;

}

首先char*没有+运算,它们之间要连接的话可以用strcat函数。

CString和string都可以和char*进行+运算,但是CString和string之间不能 CString cs = str+cstr和string st = str+cstr都是错误的。

(3)operator+

#include <iostream>

#include <atlstr.h> //在非MFC程序下使用CString类必须加的头文件

#include <string>

using namespace std;

 

int main(void)

{

 

    char *ch = "Hello World!";

    CString cstr = ch;

    string str = cstr;

    string str2 = ch; //string + char *

    str2+=ch;

    str2+=cstr;

    CString cstr2 = cstr + ch;//CString + char *

    cstr2+=ch;

    cout<<"char* : "<<ch<<endl;

    cout<<"string : "<<str<<endl;

    cout<<"string2 : "<<str2<<endl;

 

    wcout<<"CString : "<<cstr.GetString()<<endl;  //wcout输出宽字节

    wcout<<"CString2 : "<<cstr2.GetString()<<endl;//wcout输出宽字节

    system("pause");

    return 0;

}

CString和string都可以和char*进行+=运算

String可以和CString进行+=运算,但是CString+=string就不行

另外string和string之间可以+=运算,CString和CString之间也是可以的。

Char*之间是没有+=运算的,strcat可以实现同样的功能。

(4)operator []

 

#include <iostream>

#include <atlstr.h> //在非MFC程序下使用CString类必须加的头文件

#include <string>

using namespace std;

 

int main(void)

{

 

    char *ch = "Hello World!";

    CString cstr = ch;

    string str = cstr;

 

    cout<<"char* : "<<ch[2]<<endl; 

    cout<<"string : "<<str[2]<<endl;

    wcout<<"CString : "<<cstr[2]<<endl;    //wcout输出宽字节

    system("pause");

    return 0;

}

都可以使用[];但是要注意不要越界。越界会发生很严重的错误。

(4)比较

#include <iostream>

#include <atlstr.h> //在非MFC程序下使用CString类必须加的头文件

#include <string>

using namespace std;

 

int main(void)

{

 

    char *ch = "Hello World!";

    CString cstr = ch;

    string str = cstr;

    char *ch2 = "Hellr World!";

 

    cout<<"Result: "<<(ch==str)<<endl;

    cout<<"Result: "<<(cstr==ch)<<endl;

    cout<<"Result: "<<(cstr==ch2)<<endl;

    system("pause");

    return 0;

}

CString与string之间不可以进行比较,但均可以与char*进行比较,并且比较的是值,而不是地址。比较成功,结果返回1或者是0。

三.转换【实用】

string 转 CString

CString.format(”%s”, string.c_str());

char 转 CString

CString.format(”%s”, char*);

char 转 string

string s(char *);

string 转 char *

char *p = string.c_str();

CString 转 string

string s(CString.GetBuffer());

原创粉丝点击