VC++中的Unicode编程

来源:互联网 发布:程序员单词 编辑:程序博客网 时间:2024/06/04 20:38

整理了一下搜集的有关VC++中的Unicode编程问题,如char、wchar_t、TCHAR、_T等。

 

原帖:

http://rangercyh.blog.51cto.com/1444712/497922

http://bluejoe.blog.51cto.com/807902/192760

http://www.cublog.cn/u2/62117/showart_2094853.html

 

 

windows早在windows2000以后的版本里使用Unicode进行全系统开发了,也就是用于创建窗口、显示文本、进行字符串操作等所有核心函数都需用Unicode字符串。可我们在进行windows编程时基本上没有考虑这个问题,这也就所谓的兼容能力,但这是付出了代价的,比如你传递一个ANSI字符串给使用Unicode字符串的函数,那么系统首先会把字符串转换成Unicode,然后把Unicode传递给操作系统,如果希望函数返回ANSI字符串,那么系统会把Unicode字符串转换成ANSI字符串,然后将结果返回给你的应用程序。所有这些转换操作都是在你看不到的情况下进行的。这都是需要占用系统时间和内存的。

所以,今后的程序应该尽量使用Unicode来进行编码,那么如何在windows平台下进行Unicode编码呢?Microsoft为Unicode设计了Windows API。只需要在你的源代码里定义两个宏(UNICODE和_UNICODE)就来使用Unicode编码了。那么这两个宏是怎么作用的呢?

首先让我们来看看_UNICODE宏:

我们都知道标准C头文件String.h里定义了一个wchar_t的数据类型,它就是一个Unicode字符的数据类型。

typedef unsigned short wchar_t;

例如,你可以定义一个Unicode的字符数组像这样:wchar_t szBuffer[100];

该语句所占用的空间不是100个字节,而是200个。当然像strcpy、strcat、strchr等字符串操作的常见函数也是无法对Unicode使用的,所以也有一组补充的函数:

char *strcat(char *, const char *);

wchar_t *wcscat(wchar_t *, const wchar_t *);

char *strchr(const char *, int);

wchar_t *wcschr(const wchar_t *, wchar_t);

char *strcpy(char *, const char *);

wchar_t *wcscpy(wchar_t *, const wchar_t);

int strcmp(const char *, const char *);

int wcscmp(const wchar_t *, const wchar_t *);

size_t strlen(const char *);

size_t wcslen(const wchar_t *);

可以发现所有的Unicode函数开头都有wcs这几个字母,wcs是宽字符串的英文缩写——width char string。那如果我的代码中即有char型的内容,也有wchar_t的内容,比如PM让我修改一份源代码,里面以前用的是char型,而现在PM要求所有新增的代码都用wchar_t型该怎么办呢?怎么在一份源代码里即允许char的,又可以编译wchar_t呢?这个时候就可以包含TChar.h这个头文件。TChar.h里就是一组宏,用于兼容ANSI和Unicode的。这个时候你就不要调用str或wcs类的函数了,直接使用TChar.h里的通用宏就可以了。如果在编译的源代码文件里定义了_UNICODE宏,那么TChar.h里的这组宏就会引用wcs这组函数,否则就引用str的这组。

比如在TChar.h中有_tcscpy这个宏,如果没有定义_UNICODE宏,那么它就会调用strcpy函数,如果定义了就调用wcscpy函数。

你甚至可以使用TChar.h中定义的通用字符类型TCHAR。如果定义了_UNICODE,那么TCHAR的声明就是这样的:typedef wchar_t TCAHR;

如果没有定义_UNICODE,那么声明就是:typedef char TCHAR;

用TCHAR来定义字符串像这样:TCHAR szString[100];

也可以创建字符串指针:TCHAR *szError = "Error";

上面这样定义编译器会把后面的字符串当作Unicode吗?VC++编译器的默认设置是把他们当作ANSI字符的,因此,如果没有定_UNICODE,那么TCHAR就是char型的,没有什么问题,如果定义了_UNICODE,上面这行就会出错,因为给wchar_t赋值的是一个char型的字符串,那我想给它赋值为wchar_t型怎么办呢?只需要这样:TCHAR *szError = L"Error";

前面加一个大写的L就行了。告诉编译器这个字符串是一个Unicode。但现在又有问题,如果我像上面这样写,那么只有在定义了_UNICODE时才能正确编译,否则将会报错啊,谁也没看到过这种字符串的定义啊!所以为了解决这个问题在TChar.h中定义了另一个宏_T,如果定义了_UNICODE,那么_T宏定义如下:#define _T(X) L##x。如果没有定义_UNICODE,那么_T宏定义如下:#define _T(X) x。

 

那下面就让我们来看看这个宏的强大吧!以后再定义字符串不用纠结于上面的问题了,直接这么写:TCHAR *szError = _T("Error");

_T也可以用于定义单个字符,比如:szError[0] = _T('A');

VC++里设置_UNICODE宏:

Visual C++项目属性中都包含一项字符集的设置,如图所示。很容易理解,它与宏UNICODE之间存在着某种关系。VC++默认是使用多字节字符集,即ANSI。可以选择使用Unicode字符集。

 

接着让我们看看UNICODE宏吧:

Windows头文件定义了几个Unicode的数据类型:

WCHAR——Unicode字符

PWSTR——指向Unicode字符串的指针

PCWSTR——指向一个const类型的UNICODE字符串的指针

 

我们都知道windows的API经常提供两套函数原型,比如CreateWindowEx函数,就有两套原型CreateWindowExW和CreateWindowExA。初学者很少注意这个,因为我们都是直接用CreateWindowEx函数的,谁管它有几套原型呢!不过现在就让我们来关注一下吧。其实这两个原型基本上一致:

其实就是一个是Unicode的,一个是ANSI的。我们可以看看WinUser.h里对CreateWindowEx的宏定义其实是这样的:

#ifdef UNICODE

#define CreateWindowEx CreateWindowExW

#else

#define CreateWindowEx CreateWindowExA

#endif

有了这个定义,那么你只需要调用CreateWindowEx函数就行了,在需要用Unicode时就在源代码前面定义一个UNICODE就行了,不需要去修改CreateWindowEx函数。

这只是一个例子,windows提供的API大多数都是类似这样的定义,如字符串操作的一组函数windows也给出了通用的API:

lstrcat——将一个字符串置于另一个字符串的结尾处

lstrcmp——对两个字符串进行区分大小写的比较

lstrcmpi——对两个字符串进行不区分大小写的比较

lstrcpy——将一个字符串拷贝到内存中的另一个位置

lstrlen——返回字符串的长度(按字符数来计算,而不是字节数)

以上的函数当定义了UNICODE宏时会扩展为Unicode版,没有定义UNICODE时会扩展为ANSI版。

还有一些转换字符大小写的函数,比如在C库里的tolower和toupper函数是无法正确转换Unicode字符的,那么就必须调用windows定义的函数来转换:

PTSTR CharLower(PTSTR pszString);

PTSTR CharUpper(PTSTR pszString);

也可以转换单个字符:

TCHAR cLowerChar = CharLower((PTSTR)szString[0]);

还有些类似的函数,比如:

DWORD CharLowerBuffer(PTSTR pszString, DWORD cchString);

DWORD CharUpperBuffer(PTSTR pszString, DWORD cchString);

BOOL IsCharAlpha(TCHAR ch);

BOOL IsCharAlphaNumeric(TCHAR ch);

BOOL IsCharLower(TCHAR ch);

BOOL IsCharUpper(TCHAR ch);

 

注意:不管是不是用TCHAR和_T,都不能让ANSI和Unicode混存。最后从一开始就直接Unicode。

CSDN:superarhow说: 不要再使用TCHAR和_T了!如果您正开始一个新的项目,请无论如何也要顶住压力,直接使用UNICODE编码!切记!您只需要对您的组员进行10分钟的培训,记住strcpy用 wcscpy,sprintf用swprintf代替,常数前加L,就可以了!它不会花您很多时间的,带给您的是稳定和安全!相信偶,没错的!