从char/wchar_t到TCHAR

一.ANSIUNICODE简介

1.为什么要使用UNICODE

1) 可以很容易地在不同语言之间进行数据交换。

2) 使你能够分配支持所有语言的单个二进制.exe文件或DLL文件。

3) 提高应用程序的运行效率。

Windows 2000是使用UNICODE从头进行开发的,如果调用任何一个Windows函数并给它传递一个ANSI字符串,那么系统首先要将字符串转换UNICODE,然后将UNICODE字符串传递给操作系统。如果希望函数返回ANSI字符串,系统就会首先将UNICODE字符串转换ANSI字符串,然后将结果返回给你的应用程序。

进行这些字符串的转换需要占用系统的时间和内存,通过从头开始用UNICODE来开发应用程序,能够使你的应用程序更加高效地运行和更加快速的移植。

Windows 98只支持ANSI,只能开发ANSI应用程序;Windows CE使用UNICODE,完全不支持ANSI版函数。

MicrosoftCOMWin16转换成Win32时,所有COM接口方法都只能接受UNICODE字符串。

2ANSI字符和UNICODE字符

ANSI字符类型为CHAR,指向字符串的指针PSTR(LPSTR),指向一个常数字符串的指针PCSTR(LPCSTR);对应的Windows定义的UNICODE字符类型为WCHARtypedef WCHAR wchar_t),指向UNICODE字符串的指针PWSTR,指向一个常数UNICODE字符串的指针PCWSTR

定义一个ANSI字符串:"This is an ANSI string";

定义一个UNICODE字符串:L"This is a UNICODE string";

定义一个兼容适配ANSI/UICODE的字符串:__T("This is an automatic compatible string") or __TEXT("This is an automatic compatible string");

3ANSI字符和UNICODE字符串的操作

双字节(DBCS)字符集中,字符串的每个字符可以包含一个或两个字节。调用strlen()函数是无法获取字符串到底有包含了多少个有效字符,它只能告诉你到达结尾的0(null terminated)之前有多少个字节。

标准C中的strcpy,strchr,strcat等只能用于ANSI字符串,不能正确处理DBCS/UNICODE字符串,因此也提供了一组功能等价的用于UNICODE编码的补充函数。

我们来看看字符串头文件string .h中是怎样处理char*wchar_t*两个字符串版本的:

// …/Microsoft Visual Studio 8/VC/include/string.h

char *strcat(char*,constchar*);

wchar_t *wcschr(wchar_t*,constwchar_t*);

类似的还有strchr/wcschrstrcmp/wcscmpstrlen/wcslen etc. 

ANSI操作函数以str开头,例如strcpy);UNICODE操作函数则以wcs(Wide Char String)开头,例如wcscpy;MBCS操作函数以_mbs开头,例如_mbscpy。

C运行期库中的ANSI/UNICODE通用操作函数以_tcs开头,例如_tcscpy;Windows内核(KERNEL32.DLL)中的ANSI/UNICODE通用操作函数则以lstr开头,例如lstrcpy。

所有新的和未过时的函数在Windows2000中都同时提供了ANSIUNICODE两个兼容版本:ANSI版本函数结尾以A表示;UNICODE版本函数结尾以W表示。操作系统根据是否定义了字符集预编译UNICODE来调用对应版本的API

Windows XP默认字符集为gbk(包含gb2312)。wchar_t wcs[] = {0xCD, 0xCD, 0xEE, 0xFE, 0x00};表示汉字“屯铪”。参考在线汉字编码查询


二.ANSI/UNICODE通用字符/字符串类型TCHAR/LPTSTR/LPCTSTR

1._UNICODE/UNICODE宏

_UNICODE宏用于C运行期头文件(如下_tprintf示例),UNICODE宏则用于Windows头文件(如上winnt.h)。当使用VC编译代码模块时,通常必须同时定义或不定义这两个宏

如果定义了_UNICODE/UNICODE宏,若要生成一个UNICODE字符串,字符串前要加L宏以区别于ANSI C字符串,用于告诉编译器该字符串应该视作UNICODE编译处理。但是如果没有定义_UNICODE/UNICODE宏则L编译出错,为了解决这个问题,必须用到下文提到的兼容通配宏(__TEXT/__T)。使用这些宏后,无论源文件有没有定义_UNICODE/UNICODE宏都不会出现编译错误。

2.__TEXT/TEXT宏(winnt)

winnt.h中定义了__TEXT宏,用于做UNICODE环境的自适应。对于非UNICODE环境,__TEXT宏不对参数quote做任何处理;对于UNICODE环境,__TEXT宏在字符串quote前添加L宏,以表示宽字符串(wchar_t[])

// #include <winnt.h>

typedef CHAR char;

typedef WCHAR wchar_t;

 

#ifdef UNICODE                   // r_winnt

typedef WCHAR TCHAR, *PTCHAR;

typedef WCHAR *LPWSTR;

typedef LPWSTR LPTSTR;

typedef CONST WCHAR *LPCWSTR;

typedef LPCWSTR LPCTSTR;


#define__TEXT(quoteL##quote // r_winnt

#else  /* UNICODE */             // r_winnt

typedef CHAR TCHAR, *PTCHAR;

typedef CHAR *LPSTR;

typedef LPSTR LPTSTR;

typedef CONST CHAR *LPCSTR,

typedef LPCSTR LPCTSTR;


#define __TEXT(quotequote

#endif/* UNICODE */            // r_winnt


#define TEXT(quote__TEXT(quote)  // r_winnt


3.__T/_T/_TEXT宏(tchar)

MicrosoftVC提供了tchar.h头文件用于字符集的自适应(To be used for compatibility between single-byte, multi-byte and Unicode text models)。对常用的函数做了T定义,例如_tprintf

// #include <tchar.h>

#ifdef _UNICODE

#define __T(x)     L##x

typedef wchar_t    TCHAR;

#define _tprintf   wprintf

#else  /* ndef _UNICODE */

#define __T(x)     x

#define _tprintf   printf

#endif /* _UNICODE */


#define _T(x __T(x)

#define _TEXT(x)   __T(x)


4.ANSI与UNICODE的一些互转QA

Q1:如何判断一个文本文件是ANSI还是Unicode

A1:如果文本文件的开头两个字节是0xFF0xFE,那么就是Unicode,否则是ANSI

Q2:如何判断一段字符串是ANSI还是Unicode

A2:用IsTextUnicode进行判断。IsTextUnicode使用一系列统计方法和定性方法,以便猜测缓存的内容。由于这不是一种确切的科学方法,因此 IsTextUnicode有可能返回不正确的结果。

Q3:如何在UnicodeANSI之间转换字符串?

A3Windows函数MultiByteToWideChar/mbstowcs函数用于将多字节字符串转换成宽字符串,函数WideCharToMultiByte/wcstombs将宽字符串转换成等价的多字节字符串。


三.ANSI/UNICODE字符串通用函数lstrcpy/lstrcmp/lstrcat/lstrlen

ntdll.dll中实现了许多CRT基本函数:

strlen/wcslenstrcpy/wcscpystrncpy/wcsncpystrcat/wcscatstrncat/wcsncatstrcmp/wcscmpstrncmp/wcsncmpstrchr/wcschrstrrchr/wcsrchrstrstr/wcsstrsprintf/swprintfstrtol/wcstolstrtoul/wcstoul

kernel32.dll(依赖ntdll.dll)中实现了:

lstrlen(lstrlenA/lstrlenW)lstrcpy(lstrcpyA/lstrcpyW)lstrcpyn(lstrcpynA/lstrcpynW)lstrcat(lstrcatA/lstrcatW)lstrcmp(lstrcmpA/lstrcmpW)lstrcmpi(lstrcmpiA/lstrcmpiW)

///Microsoft Visual Studio 8/VC/PlatformSDK/Include/Winbase.h(已包含在windows.h中)

WINBASEAPI

LPSTR

WINAPI

lstrcpyA(

   __out LPSTR lpString1,

   __in LPCSTR lpString2

   );

WINBASEAPI

LPWSTR

WINAPI

lstrcpyW(

   __out LPWSTR lpString1,

   __in LPCWSTR lpString2

   );


#ifdef UNICODE

#define lstrcpy lstrcpyW

#else

#define lstrcpy lstrcpyA

#endif // !UNICODE


四.使用shlwapi头文件中定义的函数StrCat/StrCmp/StrCpy

Shlwapi.dll(依赖kernel32.dll)是UNCURL地址动态链接库文件,用于注册键值和色彩设置。操作系统字符串函数常常被大型应用程序(比如操作系统的外壳进程Explorer.exe)所使用,由于这些函数使用较多,在应用程序运行时,它们可能已经被装入RAM(类似嵌入式中的高频I/O常驻IRAM/DRAM),这将有助于稍稍提高应用程序的运行性能。

注意:使用StrCatStrCmpStrCpy etc时,需要在代码文件开头引入头文件和静态库:

#include <Shlwapi.h>

#pragma comment(lib,"Shlwapi.lib")


// …/Microsoft Visual Studio 8/VC/PlatformSDK/Include/Shlwapi.h

LWSTDAPI_(LPWSTR)  StrCatW(LPWSTRpsz1LPCWSTRpsz2);

LWSTDAPI_(int)  StrCmpW(LPCWSTRpsz1LPCWSTRpsz2);

LWSTDAPI_(LPWSTR)  StrCpyW(LPWSTRpsz1LPCWSTRpsz2);


#ifdef UNICODE

#define StrCat     StrCatW

#define StrCmp     StrCmpW

#define StrCpy     StrCpyW

#else

#define StrCat     lstrcatA

#define StrCmp     lstrcmpA

#define StrCpy     lstrcpyA

#endif  // !UNICODE

由上可以看出StrCpy调用的是lstrcpyStrCat调用的是lstrcatStrCmp调用的是lstrcmp


五.MFC动态字符串类CString

// …/Microsoft Visual Studio 8/VC/atlmfc/include/afx.h

一个CString对象由可变长度的一队字符组成。CString使用类似于Basic的语法提供函数和操作符。连接和比较操作符以及简化的内存管理使CString对象比普通字符串数组容易使用。

CString是基于TCHAR数据类型的对象。如果在你的程序中定义了符号_UNICODE,则TCHAR被定义为类型wchar_t,即16位宽字符类型;否则,TCHAR被定义为char,即8位ANSI字符类型。

UNICODE方式下,CString对象由16位字符组成;非UNICODE方式下,CString对象由8位字符组成。 而VS2005默认TCHARwchar而不是char。

当不使用_UNICODE时,CString是多字节字符集(MBCS,也被认为是双字节字符集[DBCS])。注意,对于MBCS字符串,CString仍然基于8位字符来计算,返回,以及处理字符串,并且你的应用程序必须自己解释MBCS的开始和结束字节。

CString提供 operator LPCTSTR来在 CString LPCTSTR 之间进行转换。

有关CString的操作请参考MSDN MFC类库。参考源码文件AFX.HAFX.INLSTRCORE.CPPSTREX.CPP


六.更安全的C语言字符串处理函数 Strsafe.h

// …/Microsoft Visual Studio 8/VC/PlatformSDK/Include/strsafe.h

注意:使用StringCchCopy/StringCchPrintf时要#include<Shlwapi.h>.

STRSAFEAPI是为了解决现有的 C语言运行时函数的代码太容易产生的内存溢出问题。当我们引用 strsafe系列函数时,原有的 C 语言字符串处理函数都将被自动进行 #undef处理。调试过程中的警告或出错信息将会告诉我们哪些函数哪些不安全,哪些已经被相应的 strsafe系列函数取代了。 

1.不赞成使用不安全的函数,以避免产生编译错误

2.如果你不要安全处理,你可以在包含strsafe.h头文件之前定义STRSAFE_NO_DEPRECATE

#define STRSAFE_NO_DEPRECATE

#ifdef DEPRECATE_SUPPORTED

// First all the names that are a/w variants (or shouldn't be #defined by now anyway).

#pragma deprecated(strcpy)

#pragma deprecated(wcscpy)

#pragma deprecated(lstrcpy)

#pragma deprecated(StrCpy)

类似的Strcat/wcscat/lstrcat/StrCatsprintf/wsprintf。

================================================================================

以下是D3D中预编译头文件dxstdafx.h

#pragma warning(disable : 4996 )//将报警置为无效

#include <strsafe.h>

#pragma warning(default : 4996 )//将报警置为默认

有关#pragma warning请参考《关于#pragma warning

================================================================================

以下是D3DVS2003移植到VS2005时遇到的安全警告:

warning C4996: 'wcscpy' was declared deprecated

see declaration of 'wcscpy'

Message: 'This function or variable may be unsafe.

Consider using wcscpy_s instead. To disable deprecation, use _CRT_SECURE_NO_DEPRECATE. See online help for details.'

warning C4995: 'lstrcpy': name was marked as #pragma deprecated

warning C4995: 'wsprintf': name was marked as #pragma deprecated

3.推荐使用新的安全可靠的TRSAFEAPI

STRSAFEAPI

StringCchCopyA(

__out_ecount(cchDestSTRSAFE_LPSTR pszDest,

__in size_t cchDest,

__in STRSAFE_LPCSTR pszSrc);


STRSAFEAPI

StringCchCopyW(

            __out_ecount(cchDestSTRSAFE_LPWSTR pszDest,

            __insize_t cchDest,

            __inSTRSAFE_LPCWSTR pszSrc);


#ifdef UNICODE

#define StringCchCopy StringCchCopyW (WWideUnicode)

#else

#define StringCchCopy StringCchCopyA (AANSI)

#endif // !UNICODE


#undef strcpy

#define strcpy     strcpy_instead_use_StringCbCopyA_or_StringCchCopyA;


#undef wcscpy

#define wcscpy     wcscpy_instead_use_StringCbCopyW_or_StringCchCopyW;


#undef wsprintf

#define wsprintf   wsprintf_instead_use_StringCbPrintf_or_StringCchPrintf;


// Then all the windows.h names - we need to undef and redef based on UNICODE setting

#undef lstrcpy//取消已定义的宏

#pragma deprecated(lstrcpy)//安全警告


#ifdef UNICODE//使用UNICODE编程

#define lstrcpy   lstrcpyW//重定义

#else

#define lstrcpy   lstrcpyA // 重定义

#endif

类似的有对lstrcat/wsprintf/wvsprintf#undef#pragma deprecated#define

#undef lstrcpy

#define lstrcpy    lstrcpy_instead_use_StringCbCopy_or_StringCchCopy;


// Then the shlwapi names - they key off UNICODE also.

#undef StrCpy

#pragma deprecated(StrCpy)


#ifdef UNICODE

#define StrCpy StrCpyW

#else

#define StrCpy lstrcpyA

#endif

类似的有#undef StrCpyA /StrCpy /StrCatA /StrCat /StrNCat /StrCatN以及对StrCpy/StrCat/StrNCat#undef#pragma deprecated#define


#undef StrCpy

#define StrCpy     StrCpy_instead_use_StringCbCopy_or_StringCchCopy;

// Then all the CRT names - we need to undef/redef based on _UNICODE value.

----------------------------------------------------------------------------------------------------

以下为StringCchCopy/StringCbCopy的MSDN参考:

StringCchCopy/StringCbCopy is a replacement for the following functions:

    • strcpy, wcscpy, _tcscpy
    • lstrcpy
    • StrCpy

----------------------------------------------------------------------------------------------------


七.VC编译UNICODE版本

VC6.0支持Unicode,但在缺省安装情况下,没有把相关的部件安装上去,所以第一步要安装相关的组件。从安装向导中选择自定义,在选择要安装的组件清单时,把VC里面带unicode的子项全部选中安装即可。

如果采用默认安装,后期需要编译发布Unicode版本,在“Project Setting→C/C++CategoryPreprocessorPreprocessor definitions”处使用_UNICODE UNICODE替换_MBCS

程序运行可能会提示找不到MFC42U.DLLMFC42UD.DLLMFCO42UD.DLLUUnicodeDDebugOOle)等,这是由于VC++安装的时候没有选择UNICODE支持,导致没有安装debug版本的MFC42*.DLL。如果只是运行需要的话,可以从VC安装盘(Visual Studio 6安装包目录/VC98/REDIST/MFC42U.DLLVisual Studio 6安装包目录/VC98/DEBUG/MFC*.DLL)或其它机器copy过来,如果编译使用,建议还是重装来得实在。


参考:

VC中的__T

VC6 UNICODE编程总

有关UNICODEANSI字符集和相关字符串操的总结

《StringCbCopy和StringCchCopy函数》

《将多字节环境移植到Unicode环境》


网页编码就是那点事

《字符编解码的故事(ASCII/ANSI/Unicode/UTF-8)》

《字符编码笔记:ASCII,Unicode和UTF-8》


《ANSI/UTF8/Unicode/ASCII编码的区别》

《ANSI/Unicode/UTF-8三种编码模式的区别》

你可能感兴趣的:(unicode,ansi,TCHAR,__T,__TEXT)