字节和字符(ANSI UNICODE)

之前对字节和字符不怎么了解,偶看了本VC的书,讲了点相关的,所以先记下来;

字符与编码

字符处理是个比较常见的问题,另外由于字符的区域性,又带来了字符编码的问题。同样的字符可能具有不同的编码,因此,编码之间的转换也是一个要考虑到的问题。

1  字节与字符

我们通常认为,字节(byte)和字符(character)是一致的,1个字节可以表示1个字符,1个字符占用1个字节,如:

 
 
  1. char msg[] = "C++";  
  2. printf("%d\r\n"sizeof(msg));  
  3. for(int i = 0; i < sizeof(msg) / sizeof(msg[0]); i++)  
  4. {  
  5.     printf("[0x%X]%c\r\n", (unsigned char)msg[i], msg[i]);  

输出结果:

 
 
  1. 4  
  2. [0x43]C  
  3. [0x2B]+  
  4. [0x2B]+  
  5. [0x0] 

新的挑战来自于中文这些多字节的字符,不妨考虑如下代码:

 
 
  1. char msg[] = "学习C++";  
  2. printf("%d\r\n"sizeof(msg));  
  3. for(int i = 0; i < sizeof(msg) / sizeof(msg[0]); i++)  
  4. {  
  5.     printf("[0x%X]%c\r\n", (unsigned char)msg[i], msg[i]);  

结果输出:

 
 
  1. 8  
  2. [0xD1]?  
  3. [0xA7]?  
  4. [0xCF]?  
  5. [0xB0]?  
  6. [0x43]C  
  7. [0x2B]+  
  8. [0x2B]+  
  9. [0x0] 
可以看出,中文完全乱了。一个英文字符对应于一个字节,而一个中文字符则可能对应于两个字节。总之,字节与字符不是一一对应的,至于一个字符占用几个字节,则是由一张映射表来表示的,而这种映射表,就是编码表。


2  ANSI、MBCS与UNICODE

目前计算机中用得最广泛的字符集及其编码,是由美国国家标准局(ANSI)制定的ASCII码(American Standard Code for Information Interchange,美国标准信息交换码),它已被国际标准化组织(ISO)定为国际标准,称为ISO 646标准。ASCII码适用于所有拉丁文字,它用7位二进制数进行编码(其最高位(bit7)被用做奇偶校验位),可以表示128个字符。

第0~32号及第127号(共34个)是控制字符或通信专用字符,如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BEL(振铃)等。

第33~126号(共94个)是字符,其中第48~57号为0~9 10个阿拉伯数字;65~90号为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等。

ASCII码占用一个字节,准确地说,是7个比特。由于汉字出现在ASCII码之后,所以汉字的编码必须兼容ASCII码。还有一个问题就是,汉字的数目很多,用简单的1个字节根本无法表达(1个字节,撑死了只能表示256个字符,除掉ASCII码,只有128个可用的了)。因此聪明的中国人决定采用2个字节来表达一个汉字。由于ASCII码占用7位的历史原因,所以这种聪明的编码方式规定:对于连续的2个字节,只有在2个字节的bit7都是1的情况下,才认为这2个字节合起来表示一个汉字。这样不同长度混排的编码方法,通常被叫做"MBCS(Muilti-Bytes Charecter Set,多字节字符集)",如"中文ABC"这串文本,它所占用的字节数可能就是:2×2+3=7,如图4-12所示。

 
图4-12  MBCS下中文与字
母的存储长度

这样的编码挺好,而且与ASCII码兼容。这种编码被称做GB2312(国标2312,GB就是国标的简写)。GB2312后来又扩展成了GBK(国标扩展码),甚至GB18030。此外,不同的国家和地区都制定了不同的编码标准,如:BIG5、JIS等编码。不同编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字存储在同一段编码的文本中,UNICODE由此浮出水面。

为了使国际间信息交流更加方便,国际标准化组织(ISO)制定了 UNICODE 字符集,为各种语言中的每一个字符设定了统一并且唯一的数字编号,以满足跨语言、跨平台进行文本转换、处理的要求。

UNICODE开始制订时,计算机的存储器容量极大地发展了,也就是说空间再也不成为问题了。于是ISO直接规定必须用2个字节,也就是16位来统一表示所有的字符,对于ASCII里的那些"半角"字符,UNICODE保持其原编码不变,只是将其长度由原来的8位扩展为16位,如英文字母"A",其编码就会变成"00000000 01100001"。很显然,由于"半角"英文符号只需要用到低8位,所以其高8位永远是0。因此这种大气的方案在保存纯英文文本时会浪费一倍的空间。而其他文化和语言的字符则全部重新统一编码。如:"中"的UNICODE为"01001110 00101101"。

这样一来,没那么多的麻烦事了,所有的字符都固定占用2个字节。如"中文ABC",它所占用的字节数就是:5×2=10,如图4-13所示。所以,使用UNICODE编码进行存放的字符也被称做宽字节字符。

 
图4-13  UNICODE下中文与字母的存储长度
在标准C++中,可以这样定义一个MBCS或者ANSI的字符串,即:
 
 
  1. char msg[] = "学习C++"

定义一个UNICODE的字符串,可以用L前缀,即:

 
 
  1. wchar_t msg[] = L"学习C++"

UNICODE统一了天下,但是客观事实是,所有的应用程序(包括航空公司的订单系统)不会一夜之间换用UNICODE编码,所以程序员面对的新问题是:如何编写自适应编码的程序?如何完成各种编码之间的转换?


3  TCHAR、_T与_TEXT

计算机最初使用的不是UNICODE,最初的系统和程序都采用的是ANSI或者MBCS,那么,问题来了:我们开发程序的时候,是采用char还是wchar_t?观察如下代码:

 
 
  1. char msg[] = "学习C++"

当我们准备采用wchar_t时,则需要改成:

 
 
  1. wchar_t msg[] = L"学习C++"
太麻烦了!幸运的是,Visual C++的开发者预知到了这样的麻烦,他们从而为此造出了一批宏,这些宏看起来乱七八糟的,但是确实解决了我们的问题,且看一段代码:
 
 
  1. TCHAR msg[] = _T("学习C++"); 

这段代码中,msg到底是char数组还是wchar_t数组呢?Visual C++的解释是,如果需要它是char数组,那么它就是char数组;否则,它就是wchar_t数组。这样的魅力来源于宏TCHAR的定义:

 
 
  1. typedef   unsigned   char   CHAR;  
  2. typedef   unsigned   wchar_t   WCHAR;   
  3.  
  4. #ifdef   UNICODE  
  5.     typedef   wchar_t   TCHAR;   
  6. #else  
  7.     typedef   unsigned   char   TCHAR;  
  8. #endif 

由此可以看出,CHAR实际上就是unsigned char,WCHAR为wchar_t,而TCHAR根据是否支持UNICODE而不同。宏UNICODE由此变得尤其重要,当我们定义了UNICODE宏,就相当于告诉了编译器:我准备采用UNICODE版本。这个时候,TCHAR就会摇身一变,由unsigned char变成了wchar_t。

Visual C++项目属性中都包含一项字符集的设置,如图4-14所示。很容易理解,它与宏UNICODE之间存在着某种关系(还有宏MCBS,为了简化讨论,在此省略)。

 
(点击查看大图)图4-14  在Visual C++中设置项目采用的字符集

前面分析了TCHAR,接下来再来看看_T的含义:

 
 
  1. #define _T(x)       __T(x)  
  2. #define _TEXT(x)    __T(x)  
  3.  
  4. #ifdef   UNICODE  
  5.     #define __T(x)      L ## x  
  6. #else  
  7.     #define __T(x)      x  
  8. #endif 

同理,当UNICODE宏被定义,"_T("学习C++")"会解释成"L"学习C++"",否则它只将其解释成""学习C++""。

因此,我们建议这样书写代码:

 
 
  1. TCHAR msg[] = _T("学习C++"); 

除TCHAR之外,Windows还定义了一系列的包含T的宏:

 
 
  1. #ifdef  UNICODE   
  2.     typedef WCHAR TCHAR, * PTCHAR ;  
  3.     typedef LPWSTR LPTCH, PTCH, PTSTRLPTSTR ;   
  4.     typedef LPCWSTR LPCTSTR ;        
  5. #else  
  6.     typedef char TCHAR, * PTCHAR ;    
  7.     typedef LPSTR LPTCH, PTCH, PTSTRLPTSTR ;   
  8.     typedef LPCSTR LPCTSTR ;     
  9. #endif 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值