揭开字符的面纱(二)

上面一节提到了字符的表示方式,这一节主要讲他们的由来以及微软给出的解决方案。
在《Windows核心编程中》有提到,从Windows NT开始,Windows的所有版本都完全用Unicode来构建。这个意思就是说,即使你用的是ASCII字符或者ASCII字符串(一个字符一个字节),都将在内部转换成Unicode字符。在大部分的Windows的API中都有xxxxxxA、xxxxxxW以及xxxxxxEx等三个前缀相同的版本,后缀为A表示使用的ANSI版本(不是ASCII),后缀为W表示使用Unicode版本(w的意思是wide),以及中间兼容版本Ex。若使用_UNICODE宏和UNICODE宏,Ex就就用Unicode版本,反之则使用ANSI版本,这是微软想到解决兼容的一种办法。

自从VC6.0开始,就已经支持UNICODE字符集,但是VC6.0默认的是“多字节字符集”(MBCS),使用UNICODE字符集需要自行在项目中定义UNICODE宏和_UNICODE宏;然而VS.NET2002以及更高版本默认的却是UNICODE字符集,即定义了UNICODE宏和_UNICODE宏,除非你在项目属性中人工指定使用“多字节字符集”(MBCS),那样就不会定义UNICODE宏和_UNICODE宏。

VS2013的MFC更是完全废除了MBCS,使用MBCS会给编译错误信息:MBCS is depreciated。

之前有提到过ANSI字符集,也有很多套,但是计算机怎么知道要用哪套字符集呢?于是提出了一套“治标不治本”的方案,这就是“ANSI代码页”。每种字符集规定一个数字作为唯一标识符,叫做“代码页”,更换这个唯一标识符就切换字符集。例如ASCII的代码页是437,latin-1的代码页是1252,GB2312-80的代码页是20936,GBK的代码页是936,BIG-5的代码页是950,GB18030的代码页是54936等等。

起初Windows就是用“ANSI代码页”切换方案,字符串用char数组来存储,叫做“多字节字符集(MBCS)”,从上一篇知道,这么叫的原因是因为有些字符不止占一位,一个GBK汉字可能占两位。

切换代码页是治标不治本,可能在这台计算机上能够编译成功,但是到了别人电脑,使用了不一样的代码页,就只能看到一堆乱码。于是为了统一文字,Unicode字符集出现了。【ISO国际标准】根据ISO的C标准和C++标准,字符类型有两种:一种叫做“窄字符”(narrow character),用char表示,每个单位占一个字节;另一种叫做“宽字符”(wide character),用wchar_t表示,每个单位占的字节数必须多于char,但没有规定具体占几个字节。窄字符和宽字符的表示方式分别是:char s[] = “narrow character” 和 wchar_t ws[] = L"wide character" 。


微软在C标准之外,私自定义了“C风格窄字符串”和“C风格宽字符串”:

// 这是C风格窄字符串,以'\0'结尾的窄字符数组
#define CONST const
typedef char CHAR;
typedef CHAR *NPSTR, *LPSTR, *PSTR;
typedef CONST CHAR *LPCSTR, *PCSTR;
// 这是以L'\0'结尾的宽字符数组
#define CONST const
typedef wchar_t WCHAR;
typedef WCHAR *NWPSTR, *LPWSTR, *PWSTR;
typedef CONST WCHAR *LPCWSTR, *PCWSTR;

为了在ANSI字符集和Unicode字符集之前切换,还定义一种TCHAR字符类型,在<winnt.h>中定义如下:

typedef wchar_t WCHAR;
#ifdef  UNICODE
typedef WCHAR TCHAR, *PTCHAR;
typedef LPWSTR PTSTR, LPTSTR;
typedef LPCWSTR PCTSTR, LPCTSTR;
#else
typedef char TCHAR, *PTCHAR;
typedef LPSTR PTSTR, LPTSTR;
typedef LPCSTR PCTSTR, LPCTSTR, PCUTSTR, LPCUTSTR;
#endif

还定义了TEXT宏:

#ifdef  UNICODE
#define __TEXT(quote) L##quote
#else
#define __TEXT(quote) quote
#endif
#define TEXT(quote) __TEXT(quote)

在<tchar.h>头文件中,还定义了_T宏和_TEXT宏:

#ifdef _UNICODE
#define __T(x)      L ## x
typedef wchar_t     TCHAR;
#else
#define __T(x)      x
typedef char        TCHAR;
#endif
#define _T(x)       __T(x)
#define _TEXT(x)    __T(x)

ISO的C和C++标准也有对应的窄字符集函数printf、strcat、strcmp和宽字符集函数wprintf、strcat、strcmp等等。微软也私自在<tchar.h>中宏定义了这些函数的TCHAR风格类型:

#ifdef _UNICODE
#define _tprintf        wprintf
#define _tcscmp         wcscmp
#define _tcscat         wcscat
#else
#define _tprintf        printf
#define _tcscmp         strcmp
#define _tcscat         strcat
#endif
跨平台应该注意:
  • 【ISO国际标准】ISO的C标准和C++标准只规定了窄字符char为一个字节,宽字符wchar_t的字节数大于char。没有规定wchar_t具体占几个字节。没有TCHAR这个东西,不存在<tchar.h>头文件。
  • 【微软方案】在微软的编译器中,char每个单位占一个字节,表示ANSI字符集;wchar_t占两个字节,表示UTF-16小端模式。可以用TCHAR类型(在<tchar.h>头文件中),并用是否定义UNICODE宏和_UNICODE宏来切换。
  • 【Linux通常情况】在大多数Linux下的编译器中,char每个单位占一个字节,通常表示UTF-8字符集;wchar_t占四个字节,表示UTF-32字符集,大小端与CPU相关。没有TCHAR这种东西。
以上就是今天的总结,希望大家能够指出错误,定会加以改进。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值