在 Windows 操作系统中,Unicode 是首选的字符编码方式,因为它能够支持全球范围内的所有字符集和语言。Windows 使用 UTF-16 编码【通常被称为“宽字符”(wide characters)】来表示 Unicode 字符。UTF-16 是一种变长编码,每个字符可以编码为一个或两个 16 位的值(即一个或两个 wchar_t
类型的数据)。
数据类型和定义
为了在 Windows 编程中更方便地使用宽字符,Windows SDK 提供了一些相关的数据类型和定义。头文件 WinNT.h 还定义了以下 typedef。
typedef wchar_t WCHAR;
若要声明宽字符文本或宽字符字符串字面量,请将 L 置于文本前面。
wchar_t a = L'a';
wchar_t *str = L"hello";
通过以下例子查看加L和不加L的区别:
#include <iostream>
#include <windows.h>
#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" ) // 设置入口地址
int main() {
MessageBoxA(NULL,"Hello Wolven","Hey",MB_OK);
MessageBoxW(NULL, L"Hello World", L"Hello", MB_OK);
system("pause");
}
在反汇编界面查看字符串地址。
对比:
不加L字符串每个字符占一字节,加上L的字符串一个字符占两个字节。
Unicode 和 ANSI 函数
Microsoft 在引入 Unicode 支持时,为了兼容现有的 ANSI 代码和逐步过渡到 Unicode,提供了两套并行的 API:一套用于 ANSI 字符串(8 位字符),另一套用于 Unicode 字符串(UTF-16 编码的宽字符)。这种设计使得开发者可以根据需要选择使用 ANSI 或 Unicode 版本。
例如,有两个函数可以设置窗口标题栏的文本:
-
SetWindowTextA 采用 ANSI 字符串。
-
SetWindowTextW 采用 Unicode 字符串。
在内部,ANSI 版本将字符串转换为 Unicode。 Windows 标头还定义了一个宏,该宏在定义预处理器符号 UNICODE
或 ANSI 版本时解析为 Unicode 版本。
#ifdef UNICODE
#define SetWindowText SetWindowTextW
#else
#define SetWindowText SetWindowTextA
#endif
新应用程序应始终调用 Unicode 版本。 许多世界语言都需要 Unicode。 如果使用 ANSI 字符串,则不可能对应用程序进行本地化。 ANSI 版本的效率也较低,因为操作系统必须在运行时将 ANSI 字符串转换为 Unicode。 根据偏好,可以显式调用 Unicode 函数,例如 SetWindowTextW 或使用宏。 最新的 Windows API 通常只有 Unicode 版本。
Visual Studio 中如何设置项目的字符集
右击项目->选择属性
项目属性页->常规->项目默认值->字符集
在右侧的 字符集 选项中,可以选择:
-
使用 Unicode 字符集:启用 Unicode 支持(推荐)。
-
使用多字节字符集:使用 ANSI 字符集。
-
未设置:默认行为,通常取决于编译器设置。
Windows SDK 提供的通用机制
除此之外Windows SDK
提供了一些通用的宏和数据类型,使得开发者可以编写与字符集无关的代码。这些宏和数据类型会根据目标平台的字符集设置(ANSI 或 Unicode),自动将字符串映射到正确的类型(char
或 wchar_t
)。这样,同一份代码可以在不同的字符集设置下编译,而无需修改代码。
为什么需要这种机制?
为了支持ANSI 和 Unicode两种字符集,Windows API 提供了两套函数,例如:
-
SetWindowTextA
:接受 ANSI 字符串。 -
SetWindowTextW
:接受 Unicode 字符串。
如果直接使用 SetWindowTextA
或 SetWindowTextW
,代码会与特定的字符集绑定,无法灵活切换。为了避免这个问题,Windows SDK 提供了一些宏和数据类型,使得代码可以根据编译时的字符集设置自动选择正确的版本。
宏 | Unicode | ANSI |
---|---|---|
TCHAR | wchar_t | char |
TEXT("x") 或 _T("x") | L"x" | "x" |
1. 通用字符类型 TCHAR
TCHAR
是一个通用的字符类型,它的定义如下:
#ifdef UNICODE
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif
-
如果定义了
UNICODE
宏,TCHAR
是wchar_t
。 -
如果没有定义
UNICODE
宏,TCHAR
是char
。
2. 通用字符串宏 TEXT
TEXT
是一个通用的字符串宏,用于定义字符串字面量。
#ifdef UNICODE
#define TEXT(quote) L##quote
#else
#define TEXT(quote) quote
#endif
-
如果定义了
UNICODE
宏,TEXT("Hello")
会被解析为L"Hello"
(宽字符字符串)。 -
如果没有定义
UNICODE
宏,TEXT("Hello")
会被解析为"Hello"
(ANSI 字符串)。
例如,以下代码:
SetWindowText(TEXT("My Application"));
解析为下列之一:
SetWindowTextW(L"My Application"); // 宽字符字符串
SetWindowTextA("My Application"); // ANSI字符串
总结
Windows SDK 提供的通用机制(如 TCHAR
、TEXT
和通用 API 宏)使得开发者可以编写与字符集无关的代码。通过定义或取消定义 UNICODE
宏,可以灵活地切换字符集模式,从而支持 ANSI 或 Unicode 字符串。这种机制提高了代码的可移植性和可维护性,是现代 Windows 编程的推荐做法。