再探C++Primer (3)关于unicode和多字节字符集

###ASCⅡ码#程序员

在ASCⅡ码制定的过程当中,对于代码是六、七、8位争议不断,因为可靠性的缘由,舍弃了6位,而因为成本问题(上世纪60年代来讲,8位代价是很是昂贵的),选择了7位编码。因而最后代码共有26个小写字母,26个大写字母,10个数字,32个符号,33个控制码,一个空格码,共128个代码组成了ASCⅡ码,成为了全球标准。windows

在小型计算机发展时期,8位一字节的标准获得巩固,因而决定以一个字节来储存字符,也就是8位编码,这样就有了128个额外的字符来补充ASCⅡ。函数

###多字节字符集DBCS#编码

256字符的字符集难以知足如中文、日文、韩文以及世界各个国家的各类文字需求,因而产生了多字节字符集(DBCS)。DBCS前128个代码就是ASCⅡ码,而较高的128个中有些还跟随有第二个字节,这两个字节在一块儿表示一个单独的字符,如一个汉字。指针

DBCS的问题在于,有些字符是单个字节组成的,例如ASCⅡ码,有些字符是两个字节组成的,这致使了字符串的字符长度不能由字节数量决定,字符串的长度须要解析后才知道,每一个字节都要被检查是否是双字节的前导字节。code

###Unicode字符集#内存

很明显的是,DBCS是一种很笨的解决方法,也产生了不少问题。做为程序员,咱们知道,与其混合使用1字节和2字节代码的DBCS,倒不如用一种全都是2字节16位编码的字符集,这就是Unicode字符集。字符串

Unicode被认为是“宽字符”,每一个Unicode的字符是16位而不是8位,在多字节字符集里咱们仍处理8位字符,而在Unicode字符集里咱们再也不处理8位字符。最开始的128个Unicode字符(0x0000到0x007F)是ASCⅡ码,而以后的128个Unicode字符(0x0080到0x00ff)是ASCⅡ码的扩展码。希腊字母表从0x0370到0x03ff,汉语日语韩语从0x3000到0x9FFF代码。扩展

Unicode的优势是只有一个字符集,避免了二义性,而其缺点是内存占比是ASCⅡ的两倍。数据类型

如今再来看char和wchar_t

char a = 'A';		//定义了一个被0x41初始化的一个字节存储空间
char *p = "hello!";		//windows是32位系统,指针p占用4个字节空间存储p的地址
						//字符串hello!存储在指针p指向的内存地址及其后6个字节,共7字节
						//包括'h''e''l''l''o''!''\0'
int s = strlen(p);		//s=6; 记住这里

wchar_t wa = 'A';		//定义了一个被0x0041初始化的两个字节存储空间
wchar_t *wp = L"hello!";	//L表明了是宽字节字符,这里,hello!包括最后的\0共占用了14个字节 
int s = strlen(wp);		//s=1; 由于strlen接受的是8位字符,它把h也就是0x0068
						//分为了0x68和0x00(内存存储顺序是高位高地址,低位低地址)
						//当读完0x68后,strlen判断读到0x00也就是字符串结束符,就结束输出了

wchar_t用了2个字节来存储字符,因此大部分处理char类型的函数都不能正常工做了,可是咱们有代替的,好比strlen的宽字节版本wcslen。在strlen定义的位置,也就是STRING.H中,strlen的声明以下:

size_t	__cdecl	strlen(cosnt char*);

而wcslen声明在了WCHAR.H中:

size_t	__cdecl	wcslen(const wchar_t*);

因此求wp的长度应该写为:

int s = wcslen(wp);		//求得s=6,即为wp中字符个数

几乎全部的字符函数都有宽字符版本,例如printf的宽字符版本为sprintf,这些都定义在了WCHAR.H中。

###关于TCHAR.H#

Unicode也有不少的缺点,好比空间的占用以及某些方面的不兼容等等,咱们但愿维护两个版本,一个用ASCⅡ码而另外一个用Unicode字符集,但并不但愿建立两套源代码文件,因此有了TCHAR.H头文件。TCHAR为须要字符或字符串做为参数的普通库函数(例如printf,strlen,sprintf,wcslen)提供了一系列的替代名称(例如_tprintf,_tcslen),这些名称是“通用”的,由于他们能够指Unicode和非Unicode版本函数。

因为TCHAR不是标准的一部分,因此定义的每一个函数和宏都有一个下划线前缀。

定义方式以下:

#ifdef	UNICODE
#define	_tcslen wcslen
#else
#define _tcslen strlen

以此类推其余函数定。

THCAR.H也提供了一个TCHAR类型来解决两个字符数据类型的问题。若是UNICODE标识符被定义了,

typedef	wchar_t	TCHAR;

不然的话,TCHAR就是char

typedef char TCHAR;

关于宽字符字符串前缀L的解决办法以下,若是UNICODE被定义了

#define	__T(x)	L##x

预处理将L和宏参数拼接在一块儿,例如x是hello,则L##x就是L"hello"

若是UNICODE没有被定义,则__T宏就定义以下:

#define __T(x)	x

而后

#define	_T(x) __T(x)
#define _TEXT(x) __T(x)

因此写字符串的时候只须要将字符串字面写在宏内就能够了

_TEXT("hello!");

这样若是UNICODE被定义了,字符串就解释为宽字符组成的,若是没有被定义,就解释为8位字符组成的。

###关于char16_t和char32_t

随着Unicode的发展,类型wchar_t已经不足以知足需求,好比在字符串编码时,若是有特定的符号和长度特征的类型将颇有帮助,而wchar_t的符号和长度不一样的C/C++库有不一样的规定。所以C++11新增长了char16_t和char32_t。

char16_t:无符号类型,长16位,char32_t无符号类型,长32位

前缀u和U分别指出字符字面值的类型为char16_t和char32_t。

相关文章
相关标签/搜索