全球化軟體的基石是**正確處理文字**。從早期的 ANSI (單位元組) 到 DBCS (雙位元組字元集,如 Big5, GBK, Shift_JIS),再到 Unicode (UTF-16/UTF-8),每一代演進都為了解決前一代的字碼衝突、資料交換困擾與平台相容性。本文系統整理 Windows 平台下的字元集操作精要。
| 問題 | 錯誤做法 | 正確做法 |
|---|---|---|
| 取得字元數 | `strlen(str)` → 回傳位元組數 | `_mbslen(str)` (MBCS) 或 `lstrlen(str)` (通用) |
| 指標移動 | `ptr++` → 可能切斷雙位元組字元 | `CharNext(ptr)` / `CharPrev(start, ptr)` |
| 判斷前導位元組 | 手動比對範圍 | `IsDBCSLeadByte(byte)` |
| 語意 | ANSI | Unicode | 通用 (TCHAR) |
|---|---|---|---|
| 字元型別 | `CHAR` | `WCHAR` | `TCHAR` |
| 字串指標 | `LPSTR` / `PSTR` | `LPWSTR` / `PWSTR` | `LPTSTR` / `PTSTR` |
| 常數字串指標 | `LPCSTR` / `PCSTR` | `LPCWSTR` / `PCWSTR` | `LPCTSTR` / `PCTSTR` |
// 專案設定:定義 UNICODE 和 _UNICODE (或專案屬性 → Character Set → Use Unicode Character Set)
// 1. 字串常值
TCHAR* szMsg = _TEXT("中文訊息"); // 或 _T("...")、TEXT("...")
TCHAR ch = _TEXT('中');
// 2. 通用執行期函數 (C Runtime)
_tcscpy(dest, src); // 對應 strcpy / wcscpy / _mbscpy
_tcslen(str); // 對應 strlen / wcslen / _mbslen
_tcscmp(s1, s2); // 對應 strcmp / wcscmp / _mbscmp
_tprintf(_T("Value: %s\n"), str);
// 3. Windows API 函數
CreateWindowEx(..., _T("MyClass"), _T("標題"), ...); // 宏展開為 CreateWindowExW
// 4. 緩衝區大小計算 —— 易犯錯誤點!
// 錯誤:sizeof(szBuffer) → 位元組數
// 正確:sizeof(szBuffer) / sizeof(TCHAR) → 字元數
// 分配記憶體:
TCHAR* p = (TCHAR*)malloc(nChars * sizeof(TCHAR)); // 不要 malloc(nChars)
支援區域感知、忽略大小寫/假名/符號/寬度等語意比較:
int result = CompareString(
LOCALE_USER_DEFAULT, // 或具體 LCID
NORM_IGNORECASE | NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH,
str1, -1, str2, -1
);
// result: 2=相等, 1=str1str2, 0=錯誤
| 旗標 | 效果 |
|---|---|
| `NORM_IGNORECASE` | 忽略大小寫 |
| `NORM_IGNOREKANATYPE` | 不區分平/片假名 |
| `NORM_IGNORENONSPACE` | 忽略非間距字元 (重音符等) |
| `NORM_IGNORESYMBOLS` | 忽略符號 |
| `NORM_IGNOREWIDTH` | 全形/半形視為相同 |
| `SORT_STRINGSORT` | 標點視為一般字元排序 |
// 多位元組 (ANSI/DBCS/UTF-8) → 寬字元 (UTF-16)
int nWide = MultiByteToWideChar(CP_UTF8, 0, pszMulti, -1, NULL, 0);
PWSTR pszWide = new WCHAR[nWide];
MultiByteToWideChar(CP_UTF8, 0, pszMulti, -1, pszWide, nWide);
// 寬字元 → 多位元組
int nMulti = WideCharToMultiByte(CP_UTF8, 0, pszWide, -1, NULL, 0, NULL, NULL);
PSTR pszMulti = new CHAR[nMulti];
WideCharToMultiByte(CP_UTF8, 0, pszWide, -1, pszMulti, nMulti, NULL, NULL);
// 記得 delete[] 釋放
代碼頁常數:`CP_ACP` (系統預設 ANSI), `CP_OEMCP` (DOS 代碼頁), `CP_UTF8` (65001), `CP_UTF7` (65000), `1200` (UTF-16LE), `1201` (UTF-16BE)。
字元集處理的核心原則是:**內部統一 Unicode (UTF-16/UTF-8),邊界明確轉換,拒絕隱含假設**。Windows 開發者應全面遷移至 `UNICODE` 建置、直接呼叫 `W` 版 API、使用 `std::wstring`/`std::u8string`,並建立編碼轉換工具庫集中管理代碼頁邏輯。這不僅能消除亂碼 Bug,更是打造世界級軟體的基本功。