上一篇咱们学习了Windows编程的文本及字体输出,在以上几篇的实例中也出现了一些带有“TEXT”的Windows宏定义,有朋友留言想了解一些ANSI和Unicode编程方面的内容,本章就来了解和学习一些Windows下关于ANSI和Unicode方面的编程基础。java
计算机最先在美国诞生,因此最开始都是以英语为做为交互语言,因为只有26个字母,用一个字节(范围-128 ~ 127)表示,这个范围足够表示26个由于字符和一些经常使用的控制字符,这个就是ASCII编码。所以最先的各类程序设计语言以及使用的字符串都用字节数组表示,也确实知足了编程的各类需求。可是随着计算机的普及,范围上逐渐超出了英语使用的国家,这样一来,字符编码就成了问题,由于不少国家的语言字符数目根本不能用一个字节来表示,好比咱们国家的中文,经常使用的就有四千多个,若是再加上一些不经常使用的字符,更是远远不止这些,所以一个字节的字符串编码就行不通了,那么天然而然就出现了两个字节甚至跟多字节的编码方式了。程序员
除了基本的ASCII编码外,目前经常使用的字符编码有MBCS、BG23十二、GBK、UTF-八、UTF-1六、 UTF-3二、BIG五、Base6四、Unicode等等,其实Unicode就是使用UTF-16编码。如今的全部系统都支持多字节编码,Windows98之前的对Unicode支持很差,不少内核函数都须要将字符串转换以后才能处理,从Windows NT系统后几乎都采用了Unicode编码从新系统内核,非Unicode的编码会通过转换以后在传入内核处理。编程
在C语言诞生的时候,一样尚未遇到多字节字符串问题,固然也没有Unicode等这些编码,标准的C语言库函数处理字符串时都是ASCII编码,所以用标C函数处理多字节字符编码就存在问题,因此不一样系统都在内部进行这种字符编码的处理。那么问题来了,既然标C不支持Unicode,咱们又如何编程使用Unicode呢?咱们如何指定程序中的字符串采用ASCII仍是Unicode或者两种同时出如今一个程序里面呢? 更好的状况,咱们如何编写程序,根据本身的需求编译ASCII和Unicode(如下称宽字符)版本?本文咱们就来谈谈这个问题。在微软公司提供的C/C++编译器中提供了一个wchar_t的变量类型,这个类型其实是经过typedef定义的一个无符号16位整型数。咱们使用这个来定义宽字符版本的字符和字符串,而普通的ANSI仍是标准C语言的char来定义。小程序
宽字符串的使用windows
下面咱们对比一下ASCII和Unicode字符(串)的定义及常量的定义方式。数组
ASCII版本:微信
Char c = ‘A’; Char str[] = “hello, world”;
宽字符版本:微信公众平台
wchar_t wch = L’A’; wchar_t wstr[] = L“hello, world”;
微软的编译器经过这个大写字母“L”开头来识别后面的字符串将编译为一个Unicode的字符或字符串,注意这里的L后面不能有空格。ide
看下面的实例:函数
#include <windows.h> #include <stdio.h> int main(void) { char c = 'A'; char str[] = "hello, ANSI"; wchar_t wch = L'A'; wchar_t wstr[] = L"hello, Unicode"; printf("1 --> %c\n", c); printf("2 --> %s\n", str); printf("3 --> %c\n", wch); printf("4 --> %s\n", wstr); printf("5 --> %C\n", c); printf("6 --> %S\n", wstr); wprintf(L"7 --> %c\n", wch); wprintf(L"8 --> %s\n\n", wstr); system("pause"); return 0; }
这个小程序的输出以下:
能够看出:
用printf能够输出ANSI的字符和字符串(废话)
用wprintf能够输出Unicode字符和字符串
printf能够用大写的字母C、S,即“%C”“%S”来输出宽字符和字符串
能够看出第3和第4用printf能够输出宽字符,但宽字符串仅仅输出了字符串的第一个字符,实际上这个就是问题了,不能这样输出,第3的字符A实际上彻底是运气好,由于Unicode是双字节,因此宽字符”A”实际在是十六进制的“00 41”,而Windows系统是一个小端系统,因此在内存中的排版为“41 00 ……”,因此第一个恰好输出A。而第4只能输出一个“h”,也是由于这个缘由。字符串wstr在内存的存在形式以下如:
第一个字符是“h”,它的宽字符在内存排布(小端系统)为”68 00 …”,根据C语言规则,字符串以空字符0x00为结束符,所以使用printf和%s来输出时,系统并不知道这个h是一个宽字符,而是以此向后一直到空字符,这里恰好第二个就碰上了,所以只能输出一个“h”。
一样,scanf函数也是如此:
scanf("%s", str); //这个是C语言的正经常使用法
scanf("%s", wstr); //这个是能够工做的,可是接收结果是ANSI格式的字符串
scanf("%S", wstr); //这个能够正确接收宽字符格式的字符串
wscanf(L"%s", wstr); //这个是标准的接收宽字符格式字符串
以上的printf和scanf用%S来处理宽字符的方式是微软扩展的,不必定其余编译系统也能这样处理。
Unicode字符串支持函数
从上面咱们看出,微软的编译器对宽字符及宽字符串常量用一个大写的“L”做为前缀来高手编译,后面的字符串做为Unicode版本而不是ANSI版本。另外printf和scanf也有对于的宽字符版本函数wprintf和wscanf来处理,从MSDN咱们知道,全部关于字符/字符串都有两个版本,好比_wfopen、_getws、wcslen、wcscpy、wcscat等就是标准C函数fopen、gets、strlen、strcpy、strcat的宽字符版本。除了这些标C的宽字符函数外,Windows的API一样有ANSI和Unicode版本,好比建立窗体和空间的CreateWindowA、CreateProcessA等就是ANSI版本,而对应的CreateWindowW、CreateProcessW就是Unicode版本,他们处理的字符串类型都必须是wchar_t的字符串。
在一个程序里面,咱们可使用ANSI版本的函数来处理对应的字符串,同时也可使用Unicode版本的函数来处理wchar_t的字符串,正如上面的实例同样,但必须对应,不然可能出现编译错误,更麻烦的是有可能编译经过可是结果却不是咱们想要的,如上面的第4一条输出。
固然若是不是须要,最好不要在程序里面一下子使用ANSI,一会使用Unicode,这样对未来的移植性兼容性不好,也不利于多语种和国际化。强烈建议使用Unicode版原本编写程序,这个是一个大趋势,若是你要把PC平台的Windows程序移植到微软的嵌入式平台Win CE上的话,就必须是Unicode。微软为了简化和通用性,在Win CE平台上只支持Unicode。并且使用Unicode编码时运行效率更高,由于如今的Windows操做系统内核所有都是用Unicode版本,若是上面传入一个ANSI的,它必须先转换成Unicode字符串,再传入内部的函数处理。
同时支持两种编码
固然理想状况是若是编写统一的应用程序,在编译时想编译成ANSI就编译成ANSI版本,想编译成Unicode版本就编译成Unicode版本是最好的,这样咱们写出来的程序不论是移植性仍是通用性都最好,其实这个微软早就想到了。
微软针对标准C函数构造了一套平台相关的字符串处理宏定义,所谓平台相关就是说这些宏是微软本身定义的,只是在Windows平台下使用,不是标准里面的东西。这些定义在不一样的状况下会变成不一样的版本。若是定义了“_UNICODE”这个宏定义,Windows将在处理C/C++函数是采用Unicode版本,不然就是ANSI版本。下面咱们以strlen这个函数来看一下Windows是怎么定义的:
#ifdef _UNICODE #define _tcslen wcslen #else #define _tcslen strlen #endif
这里的_tcslen就是那个平台相关的求字符串的字符长度的宏定义,固然咱们在使用的时候把他当作函数就好了,能够看到若是定义了_UNICODE,那么_tcslen在编译时实际是连接的wcslen,不然连接strlen。如今咱们打开VS下面的头文件“tchar.h”,就能够看到不少如下划线开头的宏定义,这些都是平台相关的通用字符串处理库函数:
因此使用这些函数的时候要包含这个头文件。
另外,若是定义了“UNCODE”这个宏,Windows的API也会采用Unicode版本,不然采用ANSI版本。好比CreateWindow这个函数定义以下:
#ifdef UNICODE #define CreateWindow CreateWindowW #else #define CreateWindow CreateWindowA #endif // !UNICODE
因此实际上CreateWindow是一个宏定义而已,可是这不影响咱们把它当作函数来使用,一样其余含有字符串做为参数的Windows API也一样作了定义。
默认状况下,咱们使用VS来创建工程,_UNICODE和UNICODE这两个宏都是打开的,因此咱们用向导建立的工程都是Unicode版本的,咱们也能够从配置选项里面删除这两个定义来编译ANSI版本的程序。
如今函数的使用解决了,那么如何来定义字符以及字符串的变量类型已常常量,使得_UNICODE和UNICODE定义也能影响类型和常量呢?微软一样使用了一系列的定义来解决这个问题。TCHAR是做为字符、字符串的变量类型,等价于char和wchar_t,若是定义了UNICDOE,TCHAR其实是wchar_t,不然就是char,这个在winnt.h中能找到。
对字符串常量,VS定义了TEXT、__TEXT,在tchar.h中,还定义了_T等好几种方式,只要定义了UNICODE,则这些宏定义就是Unicode,不然就是ANSI版本。所以咱们之后在编写程序时,应该充分用这些宏来定义字符串类型变量,常量以及处理函数。下面是一个推荐的简单实例:
#include <windows.h> #include <tchar.h> int _tmain(void) { TCHAR c = TEXT('A'); TCHAR buf[16]; TCHAR *str = TEXT("hello, world!"); _tprintf(TEXT("1 --> %c\n"), c); _tprintf(TEXT("2 --> %s\n"), str); _tscanf(_T("%s"), buf); _tprintf(_T("%s\n"), buf); _tsystem(TEXT("pause")); return 0; }
在这个实例中,全部可能用到字符串的函数都采用通用的函数,能正确的编译Unicode版本和ANSI版本。
Unicode和ANSI字符串转换
有时候咱们可能仍是会出现不一样编码之间的转换,这是咱们能够采用Windows提供的API来完成。
MultiByteToWideChar函数和WideCharToMultiByte函数,这两个函数能够在ANSI和Unicode字符串之间来回转换。他们的参数有不少类似之处,原型为:
int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar); int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar);
具体用法能够参考MSDN,网上也能找到大量的使用说明和实例,这里就再也不叙述。
下面给一个实例来演示ANSI和Unicode之间的转换:
#include <windows.h> #include <tchar.h> #include <stdio.h> int _tmain(void) { int nwCh; char AnsiStr[] = "hello, world!"; wchar_t wszBuf[20] = {0}; //得到转换后产生多少Unicode字符,能够做为后面实际转换时传入容纳转换结果的Unicode字符数buffer大小 nwCh = MultiByteToWideChar(CP_ACP, 0, AnsiStr, -1, NULL, 0); //转换并接收结果 MultiByteToWideChar(CP_ACP, 0, AnsiStr, -1, wszBuf, nwCh); wprintf(L"nwCh = %d, %s\n", nwCh, wszBuf); int nCh; char AnsiBuf[20] = {0}; //得到转换后产生多少ANSI字符,能够做为后面实际转换时传入容纳转换结果的ANSI字符数buffer大小 nCh = WideCharToMultiByte(CP_ACP, 0, wszBuf, -1, NULL, 0, NULL, NULL); //转换并接收结果 WideCharToMultiByte(CP_ACP, 0, wszBuf, -1, AnsiBuf, nCh, NULL, NULL); printf("nCh = %d, %s\n", nCh, AnsiBuf); _tsystem(TEXT("pause")); return 0; }
请注意注释部分,该函数及能够转换,也能获取转后所需输出的存储字符个数空间的大小。运行后的输出结果:
到这里本文就结束了,下一篇将继续咱们的Windows编程系列之旅。敬请关注!
更多经验交流能够加入Windows编程讨论QQ群:454398517。
关注微信公众平台:程序员互动联盟(coder_online),你能够第一时间获取原创技术文章,和(java/C/C++/Android/Windows/Linux)技术大牛作朋友,在线交流编程经验,获取编程基础知识,解决编程问题。程序员互动联盟,开发人员本身的家。
转载请注明出处,谢谢合做!