一.ANSI和UNICODE
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版函数。
Microsoft将COM从Win16转换成Win32时,所有COM接口方法都只能接受UNICODE字符串。
2.ANSI字符和UNICODE字符
ANSI字符类型为CHAR,指向字符串的指针PSTR(LPSTR),指向一个常数字符串的指针PCSTR(LPCSTR);对应的Windows定义的UNICODE字符类型为WCHAR(typedef WCHAR wchar_t),指向UNICODE字符串的指针PWSTR ,指向一个常数UNICODE字符串的指针PCWSTR 。
ANSI "ANSI";
UNICODE L"UNICODE";
ANSI/UNICODE _T("string") or _TEXT("string");
3.ANSI字符和UNICODE字符串的操作
双字节(DBCS)字符集中,字符串的每个字符可以包含一个或两个字节。如果只是调用strlen()函数,那么你就无法知道字符串到底有多少个字符,它只能告诉你到达结尾的0之前有多少个字节。
标准c中的strcpy,strchr,strcat等只能用于ANSI字符串,不能正确处理UNICODE字符串,因此也提供了一组补充函数,功能等价,但用于UNICODE码。我们来看看string .h字符串头文件中是怎样处理char*和wchar_t*两个字符串版本的:
// …/Microsoft Visual Studio 8/VC/include/string.h
char *strcat(char*,const char*);
wchar_t *wcschr(wchar_t*, const wchar_t*);
类似的还有strchr/wcschr,strcmp/wcscmp,strlen/wcslen etc. ANSI 操作函数以str开头 strcpy ,UNICODE 操作函数以wcs开头 wcscpy。
MBCS 操作函数以_mbs开头 _mbscpy
ANSI/UNICODE 操作函数以_tcs开头 _tcscpy(C运行期库)
ANSI/UNICODE 操作函数以lstr开头 lstrcpy(Windows API-KERNEL32.DLL)
所有新的和未过时的函数在Windows2000中都同时拥有ANSI和UNICODE两个版本。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宏用于C运行期头文件(如下_tprintf示例),UNICODE宏则用于Windows头文件(如上winnt.h中),当编译代码模块时,通常必须同时定义这两个宏或都不定义。
winnt.h中定义了TEXT(__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(quote) L##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(quote) quote
#endif /* UNICODE */ // r_winnt
#define TEXT(quote) __TEXT(quote) // r_winnt
Microsoft的VC提供了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)
<2>如果定义了_UNICODE,若要生成一个UNICODE字符串,字符串前要加L宏,用于告诉编译器该字符串应该作为UNICODE字符串来编译处理。但是这样又有个问题就是如果没有定义_UNICODE则编译出错。为了解决这个问题我们必须用到_T(_TEXT)宏,同winnt.h中定义的TEXT(__TEXT)宏。使用该宏后,无论源文件有没有定义_UNICODE都不会出现编译错误。
<3>ANSI与UNICODE的一些互操作QA
Q1:如何判断一个文本文件是ANSI还是Unicode?
A1:如果文本文件的开头两个字节是0xFF和0xFE,那么就是Unicode,否则是ANSI。
Q2:如何判断一段字符串是ANSI还是Unicode?
A2:用IsTextUnicode进行判断。IsTextUnicode使用一系列统计方法和定性方法,以便猜测缓存的内容。由于这不是一种确切的科学方法,因此 IsTextUnicode有可能返回不正确的结果。
Q3:如何在Unicode与ANSI之间转换字符串?
A3:Windows函数MultiByteToWideChar/mbstowcs函数用于将多字节字符串转换成宽字符串,函数WideCharToMultiByte/wcstombs将宽字符串转换成等价的多字节字符串。
三.ANSI/UNICODE字符串通用函数lstrcpy/lstrcmp/lstrcat/lstrlen
ntdll.dll中实现了许多CRT基本函数:
strlen/wcslen、strcpy/wcscpy、strncpy/wcsncpy、strcat/wcscat、strncat/wcsncat、strcmp/wcscmp、strncmp/wcsncmp、strchr/wcschr、strrchr/wcsrchr、strstr/wcsstr、sprintf/swprintf、strtol/wcstol、strtoul/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)是UNC和URL地址动态链接库文件,用于注册键值和色彩设置。因为操作系统字符串函数常常被大型应用程序比如操作系统的外壳进程Explorer.exe所使用。由于这些函数使用得很多,因此,在应用程序运行时,它们可能已经被装入RAM。这将有助于稍稍提高应用程序的运行性能。
注意:使用StrCat、StrCmp、StrCpy etc时要
#include <Shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
// …/Microsoft Visual Studio 8/VC/PlatformSDK/Include/Shlwapi.h
LWSTDAPI_(LPWSTR) StrCatW(LPWSTR psz1, LPCWSTR psz2);
LWSTDAPI_(int) StrCmpW(LPCWSTR psz1, LPCWSTR psz2);
LWSTDAPI_(LPWSTR) StrCpyW(LPWSTR psz1, LPCWSTR psz2);
#ifdef UNICODE
#define StrCat StrCatW
#define StrCmp StrCmpW
#define StrCpy StrCpyW
#else
#define StrCat lstrcatA
#define StrCmp lstrcmpA
#define StrCpy lstrcpyA
由上可以看出StrCpy调用的是lstrcpy,StrCat调用的是lstrcat,StrCmp调用的是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位字符类型。在UNICODE方式下,CString对象由16位字符组成。非UNICODE方式下,CString对象由8位字符组成。 而VS2005默认TCHAR是wchar而不是char.
当不使用_UNICODE时,CString是多字节字符集(MBCS,也被认为是双字节字符集,DBCS)。注意,对于MBCS字符串,CString仍然基于8位字符来计算,返回,以及处理字符串,并且你的应用程序必须自己解释MBCS的开始和结束字节。
CString 提供 operator LPCTSTR 来在 CString 和 LPCTSTR 之间进行转换。
有关CString的操作请参考MSDN MFC类库。参考源码文件AFX.H、AFX.INL、STRCORE.CPP和STREX.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头文件之前,
#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/StrCat,sprintf/wsprintf
以下是D3D中预编译头文件dxstdafx.h
#pragma warning( disable : 4996 ) // 将报警置为无效
#include <strsafe.h>
#pragma warning( default : 4996 ) // 将报警置为默认
有关#pragma warning请参考《关于#pragma warning》
以下是D3D从VS2003移植到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
推荐使用新的安全可靠的TRSAFEAPI:
STRSAFEAPI
StringCchCopyA(
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
__in size_t cchDest,
__in STRSAFE_LPCSTR pszSrc);
STRSAFEAPI
StringCchCopyW(
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
__in size_t cchDest,
__in STRSAFE_LPCWSTR pszSrc);
#ifdef UNICODE
#define StringCchCopy StringCchCopyW (W为Wide Unicode)
#else
#define StringCchCopy StringCchCopyA (A为ANSI)
#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。
推荐使用新的安全可靠的TRSAFEAPI:
#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。
推荐使用新的安全可靠的TRSAFEAPI:
#undef StrCpy
#define StrCpy StrCpy_instead_use_StringCbCopy_or_StringCchCopy;
// Then all the CRT names - we need to undef/redef based on _UNICODE value.
七.VC编译UNICODE版本
VC6.0支持Unicode,但在缺省安装情况下,没有把相关的部件安装上去,所以第一步要安装相关的组件。从安装向导中选择自定义,在选择要安装的组件清单时,把vc里面带unicode的子项全部选中安装即可。
如果采用默认安装,后期需要编译发布Unicode版本,在“Project SettingàC/C++(Category:Preprocessor)àPreprocessor definitions”处使用_UNICODE UNICODE替换掉_MBCS。
程序运行可能会提示找不到MFC42U.DLL、MFC42UD.DLL、MFCO42UD.DLL(U:Unicode,D:Debug,O:Ole)等,这是由于VC++ 安装的时候没有选择UNICODE支持的缘故,导致没有安装debug版本的MFC42*.DLL。如果只是运行需要的话,可以从VC安装盘(Visual Studio 6安装包目录/VC98/REDIST/MFC42U.DLL;Visual Studio 6安装包目录/VC98/DEBUG/MFC*.DLL)或其它机器copy过来,如果编译使用,建议还是重装来得实在。
参考:
《VC中的__T宏》
《VC6 UNICODE编程总结》
《有关UNICODE、ANSI字符集和相关字符串操作的总结》
分享到:
相关推荐
VC++中的char,wchar_t,TCHAR详解 在VC++中,char、wchar_t和TCHAR是三个非常重要的字符类型,它们之间的区别和使用方法是初学者需要了解的基础知识。下面我们将详细介绍这三个字符类型的概念、用法和区别。 一、...
### VC++中的char, wchar_T和TCHAR详解 在VC++开发环境中,处理文本数据时,程序员经常会遇到三种字符类型:`char`、`wchar_T` 和 `TCHAR`。这些类型的选择对于确保代码的兼容性和效率至关重要。本文将详细介绍这三...
详细的介绍了char wchar TCHAR的联系和区别,非常适合初学者了解
`char`, `_wchar_t`, `_TCHAR`, `__T()`, `L`, 宏`__T`, `TEXT`, `_TEXT`, 和 `L` 都是与字符编码和跨平台兼容性紧密相关的概念。 1. **`char`**: 这是最基本的字符类型,通常用于处理ASCII编码的单字节字符。例如...
在编译时,如果定义了`UNICODE`或`_UNICODE`宏,`TCHAR`将被映射为`WCHAR`,否则它被映射为`CHAR`。`WCHAR`是用于表示宽字符(如UTF-16)的数据类型。使用`TCHAR`可以编写同时支持多语言环境的代码,无需进行大量...
`char` 和 `wchar_t` 在计算机编程中,字符类型是处理文本数据的基础。对于不同的字符集和编码标准,编程语言提供了多种字符类型来满足需求。 - **`char`**: 这是一种基本的数据类型,在 C/C++ 中用于存储单字节...
本文将深入解析几种常见的字符串类型,包括`CString`、`LPCTSTR`、`LPTSTR`、`TCHAR`、`WCHAR`、`string`、`wchar_t`和`char`。 1. `CString`:`CString`是Microsoft MFC(Microsoft Foundation Classes)库中定义...
如果定义了`UNICODE`,`TCHAR`则等同于`wchar_t`。这意味着`TCHAR`可以在非Unicode环境下表示`char`类型,在Unicode环境下表示`WCHAR`类型,提供了一种编写既可兼容ASCII又可兼容Unicode代码的方式。这在需要编写...
TCHAR宏是根据项目属性自动替换为char或wchar_t的宏,可以使用TCHAR宏来定义变量,例如: TCHAR Buf[]; 字符串字面值也可以使用_T()宏,例如: _T("abcd") 这可以根据项目属性自动转换为wchar_t或char类型。 ...
在处理中文时有时需要进行wchar_t,char,string,wstring之间的转换。 其中char和string之间、wchar_t和wstring之间的转换较为简单,代码在vs2010下测试通过。 代码如下:#include <iostream>#include <string>#...
C++ 提供了多种字符类型,包括 char、wchar_t、TCHAR 等,每种类型都有其特点和使用场景。本文将对 C++ 字符类型进行总结,包括 char、wchar_t、TCHAR、BSTR 等类型的定义、特点和使用方法。 1. char 类型 char 是...
`LPTSTR`、`LPCTSTR`等是`TCHAR`的指针版本,根据编译环境自动调整为`char*`或`wchar_t*`。 7. 字符串转换: - `char*`到`CString`的转换可以通过直接赋值或使用`CString::Format`方法。 - `CString`到`char*`的...
当在 windows 下开发程序库时,使用 TCHAR 类型,比直接使用 char 和 wchar_t 具有更好的移植性。但是在编写动态链接库时,不能直接导出 TCHAR 类型的函数,给 TCHAR 类型函数的使用带来了一些限制。使用 texport 在...
- **TCHAR**: 这是一个宏定义,可以根据编译时的设置选择`char`或`wchar_t`。在ANSI环境下,它会定义为`char`;而在UNICODE环境下,则定义为`wchar_t`。 - **_TEOF**: 定义了EOF值,在ANSI环境下为`EOF`,在UNICODE...
TCHAR是一个宏定义,它可以根据编译器的预定义宏自动选择为`char`或`wchar_t`,以适应宽字符(Unicode)和窄字符(ASCII)的环境。`cpp-texport`是一个工具,它帮助开发者自动生成与TCHAR相关的函数调用存根,使得在...
TCHAR 是一个可移植的字符类型,在 Unicode 环境下等同于 wchar_t,在 SBCS 环境下等同于 char。wchar_t 是一个 Unicode 字符类型,用于表示 Unicode 字符。 例如,_TCHAR_类型的函数可以处理 Unicode 字符,而_...
如果定义了 `_UNICODE`,则 `TCHAR` 被定义为 `wchar_t` 类型;否则,被定义为 `char` 类型。这样做的目的是为了提供一种方式来编写既可以使用Unicode也可以使用ANSI编码的代码。 ```c++ #ifdef _UNICODE ...
TCHAR*则是在Windows SDK中定义的,根据定义 `_UNICODE` 和 `_MBCS` 宏的状态,它可以是`wchar_t*`(Unicode)或`char*`(非Unicode)。 转换方法主要有以下几种: 1. 使用QString的`toLocal8Bit()`函数: 当需要...
bool UTF8ToUnicode(const char* UTF8, wchar_t* strUnicode) { DWORD dwUnicodeLen; //转换后Unicode的长度 TCHAR *pwText; //保存Unicode的指针 // wchar_t* strUnicode; //返回值 //获得转换后的长度,并分配...
- 定义了`UNICODE`宏时,`TCHAR`等同于`wchar_t`,用于支持Unicode编码。 使用`TCHAR`可以编写兼容MBCS和Unicode的代码,无需根据不同的编码方式重写代码。 #### 3. `CString` `CString`是Visual C++ MFC类库中的...