首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Windows 字符編碼轉換全攻略:MBCS 與 Unicode 互轉的四種方法詳解

     編輯:關於C語言

Windows 開發中頻繁面臨多字節字符集(MBCS/ANSI)與寬字符(Unicode/WideChar)互轉需求。本文系統梳理四種主流轉換方案:Win32 API 兩對核心函數、ATL 便利宏、C++ 標準庫 codecvt、以及現代 MultiByteToWideChar 封裝最佳實踐。

一、 Win32 API 核心函數對(最底層、最通用)

1. 多字節 → 寬字符:MultiByteToWideChar

int MultiByteToWideChar(
    UINT CodePage,        // 代碼頁:CP_ACP(系統默認)、CP_UTF8、CP_OEMCP 等
    DWORD dwFlags,        // 標誌位:MB_PRECOMPOSED、MB_ERR_INVALID_CHARS 等,通常 0
    LPCSTR lpMultiByteStr,// 源字符串指針
    int cbMultiByte,      // 源字節數,-1 表示以 '\0' 結尾自動計算
    LPWSTR lpWideCharStr, // 目標緩衝區,NULL 時返回所需字符數
    int cchWideChar       // 目標緩衝區字符容量
);

標準兩步調用模式(必須掌握)

// 第一步:獲取所需緩衝區大小
int len = MultiByteToWideChar(CP_ACP, 0, mbStr, -1, NULL, 0);
if (len <= 0) return ERROR;

// 第二步:分配緩衝區並轉換
std::vector<wchar_t> buf(len);
int result = MultiByteToWideChar(CP_ACP, 0, mbStr, -1, buf.data(), len);
// buf[len-1] 為 L'\0',有效字符數為 result-1

2. 寬字符 → 多字節:WideCharToMultiByte

int WideCharToMultiByte(
    UINT CodePage,
    DWORD dwFlags,
    LPCWSTR lpWideCharStr,
    int cchWideChar,
    LPSTR lpMultiByteStr,
    int cbMultiByte,
    LPCSTR lpDefaultChar,   // 無法轉換時的替換字符,NULL 為 '?'
    LPBOOL pfUsedDefaultChar// 是否使用了替換字符
);

標準調用模式

int len = WideCharToMultiByte(CP_ACP, 0, wcStr, -1, NULL, 0, NULL, NULL);
if (len <= 0) return ERROR;
std::vector<char> buf(len);
WideCharToMultiByte(CP_ACP, 0, wcStr, -1, buf.data(), len, NULL, NULL);

二、 ATL 便利宏(代碼簡潔、但有內存陷阱)

#include <atlconv.h>  // 需包含 ATL 頭文件

USES_CONVERSION;  // 每次轉換前必須調用!分配棧緩衝區

wchar_t wszText[] = L"Unicode字符串";
char szText[] = "ANSI字符串";

printf("%s\n", W2A(wszText));   // 寬 → 多
wprintf(L"%s\n", A2W(szText));  // 多 → 寬

⚠️ 關鍵限制(生產環境慎用)

  • 棧內存耗盡風險:宏在棧上分配緩衝區,大循環、長字符串、遞歸調用極易棧溢出
  • 線程不安全:靜態緩衝區共享,多線程並發轉換會數據競爭
  • 不支持自定義代碼頁:隱式使用 CP_ACP
  • 調試困難:宏展開後代碼可讀性極差

建議:僅用於簡單工具、測試代碼、非熱路徑。生產代碼請使用 Win32 API 手動管理堆內存。

三、 C++11 <codecvt> 標準庫方案(跨平台、類型安全)

#include <codecvt>
#include <locale>
#include <string>

// UTF-8 ↔ UTF-16/32 (wchar_t)
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;

std::wstring wide = L"Hello 世界";
std::string utf8 = converter.to_bytes(wide);   // 寬 → UTF-8
std::wstring wide2 = converter.from_bytes(utf8); // UTF-8 → 寬

// 注意:C++17 棄用 codecvt,C++20 移除。長期項目請遷移至 ICU 或獨立庫。

四、 現代最佳實踐:RAII 封裝類(推薦生產使用)

class StringConverter {
public:
    // 多字節(指定代碼頁) → 寬字符
    static std::wstring ToWide(const std::string& mb, UINT cp = CP_UTF8) {
        if (mb.empty()) return L"";
        int len = MultiByteToWideChar(cp, 0, mb.c_str(), -1, NULL, 0);
        if (len <= 0) throw std::runtime_error("MultiByteToWideChar failed");
        std::wstring ws(len - 1, L'\0');  // -1 排除結束符
        MultiByteToWideChar(cp, 0, mb.c_str(), -1, &ws[0], len);
        return ws;
    }

    // 寬字符 → 多字節(指定代碼頁)
    static std::string ToMultiByte(const std::wstring& ws, UINT cp = CP_UTF8) {
        if (ws.empty()) return "";
        int len = WideCharToMultiByte(cp, 0, ws.c_str(), -1, NULL, 0, NULL, NULL);
        if (len <= 0) throw std::runtime_error("WideCharToMultiByte failed");
        std::string mb(len - 1, '\0');
        WideCharToMultiByte(cp, 0, ws.c_str(), -1, &mb[0], len, NULL, NULL);
        return mb;
    }

    // 常用別名
    static std::wstring AnsiToWide(const std::string& s)  { return ToWide(s, CP_ACP); }
    static std::string WideToAnsi(const std::wstring& s)  { return ToMultiByte(s, CP_ACP); }
    static std::wstring Utf8ToWide(const std::string& s)  { return ToWide(s, CP_UTF8); }
    static std::string WideToUtf8(const std::wstring& s)  { return ToMultiByte(s, CP_UTF8); }
};

五、 常見代碼頁常量

常量含義適用場景
CP_ACP系統默認 ANSI 代碼頁本地化應用、舊系統交互
CP_UTF8UTF-8跨平台、網絡傳輸、現代應用首選
CP_OEMCPOEM 代碼頁 (如中文 CP936)控制台程序、舊 DOS 應用
1200UTF-16LEWindows 內部寬字符
1201UTF-16BE網絡序寬字符

六、 避坑檢查清單

  1. 緩衝區大小計算:API 返回值包含結束符,分配時 -1;調用時傳完整長度
  2. 代碼頁一致性:轉換雙向必須使用相同代碼頁,否則數據損壞
  3. 錯誤處理:檢查返回值 <= 0,調用 GetLastError() 獲取具體錯誤碼
  4. NULL 終結符:輸入字符串必須以 '\0' 結尾,或顯式傳遞長度
  5. BOM 處理:UTF-8 文件可能帶 BOM,讀取時需手動跳過
  6. surrogate pairs:UTF-16 中補充平面字符佔兩個 wchar_t,長度計算需注意

本文出自 “平凡的日子” 博客,轉載請保留出處:原文鏈接

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved