中文字符編碼問題


為了解決程序對中文支持不好的問題(如路徑不能含中文),強制程序內的char*字符串采用utf8編碼。

編碼不統一導致的亂碼問題

因為歷史原因,C/C++語言中char*字符串可能采用各種編碼:ASCII, Latin, utf8, GBK...

所有文本文件可用的編碼,char*字符串都可以采用。

 

1 VSCode支持幾十種文本編碼

 

如果代碼中混用兩種編碼AB,導致采用A編碼的char*字符串被用B編碼解析,得到的字符串就可能出現亂碼。

一般純ASCII字符不會發生亂碼,因為絕大部分編碼的0-127部分的字符都和ASCII編碼兼容。

當我們代碼里有非ASCII字符的編碼時,統一編碼就顯得非常有必要。

 

char*編碼要求采用UTF8

目前最通用的char*編碼為utf8編碼,utf8編碼被PythonLinux等設置為默認編碼。

Windows在不同語言的版本中,char*的默認編碼是當前國家的特定編碼,比如中文版采用GBK編碼。

Windows內核使用的編碼為utf16編碼的wchar_t*字符串,調用Windows API傳入的char*字符串會被先轉換為wchar_t*字符串后再調用Unicode版本的API

 

GBK編碼存在不能表示某些字符的問題,utf8可表示所有字符。

 

為了解決編碼混亂問題,C++20專門為utf8編碼新增了std::u8stringchar8_t類型。

很多第三方庫也支持utf8編碼(當然還有部分不支持)

 

綜上

  • 后續產品中的自有C++代碼的char*字符串采用utf8編碼
  • Windows API只使用Unicode版本
  • 在遇到不支持utf8編碼的庫時,對其進行接口封裝(編碼轉換)
  • 建議函數的形參添加_u8后綴,以便調用者快速確認編碼要求

 

字符串的編碼設置

在定義字符串時,采用以下方法來指定char*字符串為utf8編碼

// 在VS2010及之后版本,支持設置字符串為UTF8編碼

#if _MSC_VER >= 1600

#pragma execution_character_set("utf-8")

#endif

 

// 在VS2015及之后版本,支持配置編譯選項:/execution-charset:utf-8

// 等同項目中所有cpp文件包含上述指令

 

// 默認str1為GBK編碼字符串,進行以上的設置后,str1為utf8編碼

const char* str1 = "中文字符串";

 

// 在VS2015后的版本,只需為字面量添加u8前綴即可得到utf8編碼的字符串

// 但Qt的翻譯更新功能不認u8前綴,導致ts文件內不包含tr(u8"abc")形式的字符串

const char* str2 = u8"中文字符串";

 

// Linux系統默認采用utf8編碼,無需做額外工作

 

采用VS2015及之后版本開發的產品中,建議設置編譯選項:/execution-charset:utf-8

 

 

系統調用的處理

系統提供的函數

命令行輸出

  1. 可對 printf / wprintf / std::cout 進行封裝以便輸出utf8字符串
  2. 直接輸出std::wstring

// 程序啟動時,需要初始化Locale以便輸出寬字符

setlocale(LC_ALL, "");

 

// 輸出std::wstring至命令行

std::wstring wstr = L"中文字符串";

std::wcout << wstr;

printf("%ls", wstr.c_str());  // %hs替代窄字符串,%ls替代寬字符串

                              // printf中%s替代窄字符串,wprintf中%s替代寬字符串

 

文件打開

  1. 采用 _wfopen / CreateFileW 等方法,傳入wchar_t字符串表示的路徑
  2. 采用std::ofstream::open(std::wstring wstr)打開

 

其它系統API

請采用windows apiUnicode版本,即帶W后綴的版本。utf8字符串可通過編碼轉換函數轉換后傳入。

CreateFileA(); // 需傳入ansi字符串,打開其它語言文件名的文件可能失敗,不允許使用

CreateFileW(); // 傳入wchar_t字符串,推薦使用

 

編碼轉換方法

產品應該在底層統一提供wchar_t字符串與char字符串的轉換方法,供自有代碼調用。

  • 不允許自有代碼對底層編碼轉換方法的調用
    • WideCharToMultiByte / MultiByteToWideChar
    • wcstombs / mbstowcs
  • 嚴格限制自有代碼對第三方庫提供的轉換代碼的調用
    • 對第三方庫本身進行更改或擴展時,可調用庫提供的轉換函數
    • 基於Qt的代碼可調用QString.toUtf8() / QString.toStdString()
  • 嚴格限制對ANSI字符串轉換函數的調用,包含:(代碼需注釋調用原因)
    • string2wstring_ansi / wstring2string_ansi
    • QString.toLocal8Bit / QString.toLatin1
    • QString::fromLocal8Bit / QString::fromLatin1
    • QTextCodec::fromUnicode

 

底層統一提供的轉換方法實現參考:(僅限產品底層進行實現,上層直接調用即可)

std::string wstring2string(const std::wstring& wstr, UINT CodePage)

{

std::string ret;

int len = WideCharToMultiByte(CodePage, 0, wstr.c_str(), (int)wstr.size(), NULL, 0, NULL, NULL);

ret.resize((size_t)len, 0);

WideCharToMultiByte(CodePage, 0, wstr.c_str(), (int)wstr.size(), &ret[0], len, NULL, NULL);

return ret;

}

 

std::string wstring2string_ansi(const std::wstring& wstr)

{

return wstring2string(wstr, CP_ACP);

}

 

std::string wstring2string_utf8(const std::wstring& wstr)

{

return wstring2string(wstr, CP_UTF8);

}

 

std::wstring string2wstring(const std::string& str, UINT CodePage)

{

std::wstring ret;

int len = MultiByteToWideChar(CodePage, 0, str.c_str(), (int)str.size(), NULL, 0);

ret.resize((size_t)len, 0);

MultiByteToWideChar(CodePage, 0, str.c_str(), (int)str.size(), &ret[0], len);

return ret;

}

 

std::wstring string2wstring_ansi(const std::string& str_ansi)

{

return string2wstring(str_ansi, CP_ACP);

}

 

std::wstring string2wstring_utf8(const std::string& str_u8)

{

return string2wstring(str_u8, CP_UTF8);

}

 

代碼示例

// QString字符串

QString qstr;

std::string str = qstr.toStdString();

std::wstring wstr = qstr.toStdWString();

String unig_str = String(qstr.toUtf8());

 

// 如果需要在后面使用const char*字符串,一定要顯式定義包含數據的變量

auto tmp_str = qstr.toUtf8();

const char* pstr = tmp_str.constData();

func_use_const_char_str(pstr);

 

// 或者下面這種形式,編譯器會自動轉換成const char*

func_use_const_char_str(qstr.utf8());

 

// 變參函數,必須顯式調用constData()轉換為const char*,因為編譯器不會自動轉換

sprintf(buf, "xxx%sxxx", qstr.toUtf8().constData());

 

 

// std::string字符串(utf8編碼)

std::string str;

std::wstring = string2wstring_utf8(str);

QString qstr = QString::fromStdString(str);

String unig_str = String(str.data());

const char* pstr = str.data();

 

 

// std::wstring

std::wstring wstr;

std::string str = wstring2string_utf8(wstr);

QString qstr = QString::fromStdWString(wstr);

String unig_str = String(wstring2string_utf8(wstr).data());

 

 

// const char*(utf8編碼)

const char* pstr;

std::string str = pstr;

std::wstring wstr = string2wstring_utf8(pstr);

QString qstr = QString::fromUtf8(pstr);

String unig_str = String(pstr);

 

 

// const wchar_t*

const wchar_t* pwstr;

std::string str = wstring2string_utf8(pwstr);

std::wstring wstr = pwstr;

QString qstr = QString::fromWCharArray(pwstr);

String unig_str = String(wstring2string_utf8(pwstr).data);

 

其它

擴展閱讀:

  1. 淺談C/C++編程中的字符編碼轉換

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM