Windows 內核自 NT 系統起原生使用 Unicode (UTF-16)。ANSI 版本 API 實際上是系統提供的薄封裝:將參數轉換為 Unicode 再調用寬字符版本,返回結果再轉回 ANSI。這層轉換帶來效能損耗、字符集相容性問題(DBCS 雙位元組字符集導致字符邊界判斷困難)以及跨語系顯示錯亂。
現代 Windows 應用強烈建議純 Unicode 開發,僅在對接舊式 ANSI 組件、讀寫特定編碼文件、網路協議要求時才進行邊界轉換。
_MBCS,添加 UNICODE、_UNICODE。wWinMainCRTStartup(GUI)或 wmainCRTStartup(控制台)。| 場景 | ANSI 寫法 | Unicode 通用寫法 |
|---|---|---|
| 字符類型 | char | TCHAR (或直接 wchar_t) |
| 字符串字面量 | "hello" | _T("hello") 或 L"hello" |
| 字符串函數 | strcpy, strlen, strcat | _tcscpy, _tcslen, _tcscat (CRT) / lstrcpy, lstrlen (Win32) |
_T() 宏定義:
#ifdef UNICODE
#define _T(x) L##x
#else
#define _T(x) x
#endif
| ANSI | Unicode | 通用 (TCHAR) | 說明 |
|---|---|---|---|
char | wchar_t | TCHAR | 基本字符類型 |
LPSTR | LPWSTR | LPTSTR | 可修改字符串指針 |
LPCSTR | LPCWSTR | LPCTSTR | 常量字符串指針 |
std::string | std::wstring | std::basic_string<TCHAR> | C++ 標準庫字符串 |
CStringA | CStringW | CString | MFC/ATL 字符串類 |
// Unicode → ANSI
std::string toAnsi(const std::wstring& wstr, UINT codePage = CP_ACP) {
if (wstr.empty()) return "";
int len = WideCharToMultiByte(codePage, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr);
std::string str(len - 1, '\0');
WideCharToMultiByte(codePage, 0, wstr.c_str(), -1, &str[0], len, nullptr, nullptr);
return str;
}
// ANSI → Unicode
std::wstring toUnicode(const std::string& str, UINT codePage = CP_ACP) {
if (str.empty()) return L"";
int len = MultiByteToWideChar(codePage, 0, str.c_str(), -1, nullptr, 0);
std::wstring wstr(len - 1, L'\0');
MultiByteToWideChar(codePage, 0, str.c_str(), -1, &wstr[0], len);
return wstr;
}
// UTF-8 專用 (CP_UTF8)
std::string toUtf8(const std::wstring& wstr) { return toAnsi(wstr, CP_UTF8); }
std::wstring fromUtf8(const std::string& str) { return toUnicode(str, CP_UTF8); }
LoadString、GetDlgItemText 等資源 API 返回 Unicode 字符串。CString str = _T("中文") 無需轉換,編譯器自動處理。| 坑點 | 現象 | 解決方案 |
|---|---|---|
sizeof 計算字符數 | Unicode 下 sizeof(TCHAR) = 2,字符數 = sizeof(buf)/sizeof(TCHAR) | 使用 _countof、wcslen、_tcslen、CString::GetLength |
| 格式化字符串 | printf("%s", wstr) 輸出亂碼 | wprintf(L"%s", wstr) 或 _tprintf(_T("%s"), str) 或 std::wcout |
| 文件讀寫 | 讀取 UTF-8 文件直接存入 CString 亂碼 | 讀取為 std::string → fromUtf8 → CString |
| 網路通訊 | 發送 CString 直接 send() 對端亂碼 | 協議層統一 UTF-8:toUtf8 → 發送 std::string 位元組流 |
| 簡繁字體回退 | 繁體系統顯示「國」字缺筆 | 字體連結:系統字體 → 微軟正黑體/新細明體;或指定 MS Shell Dlg 邏輯字體 |
// 內部統一使用 UTF-8 std::string (或 std::u8string C++20)
std::string internal_utf8 = u8"中文"; // C++20 char8_t
// Windows API 邊界轉換封裝
namespace win32 {
std::wstring toWide(const std::string& utf8) {
return fromUtf8(utf8); // 內部實現
}
std::string toUtf8(const std::wstring& wide) {
return toUtf8(wide);
}
}
// 文件 I/O 使用 std::filesystem::path (原生支援 Unicode 路徑)
std::filesystem::path p = LR"(\\?\C:\Very\Long\Path\中文.txt)";
std::ofstream ofs(p, std::ios::binary); // C++17 支援宽字符路徑
_MBCS,定義 UNICODE/_UNICODE,設定入口點。TCHAR,字面量用 _T(),函數用 _tcs* 族。std::string,配合 std::filesystem、char8_t、std::format (C++20) 徹底擺脫 TCHAR 宏依賴。