Unicode

追求寬字元字串和一般字元字串的轉換效率

在 Windows 之中,想將標準字元 (char) 字串轉換成寬字元 (wchar_t) 字串,通常會使用 MultiByteToWideChar() 這個 Win32 API。這個 API 的 prototype 如下:

int MultiByteToWideChar(
    UINT CodePage,              // [in] 說明輸入字串的編碼。本文假設都為 UTF-8。
    DWORD dwFlags,              // [in] 選項旗標。本文不討論。
    LPCSTR lpMultiByteStr,      // [in] 指向輸入字串的指標。
    int cchMultiByte,           // [in] 指定要處理輸入字串 char 個數。-1 表示處理到結尾的空字元。
    LPWSTR lpWideCharStr,       // [out; optional] 輸出的 buffer。
    int cchWideChar             // [in] 描述 lpWideCharStr 的 buffer 大小,單位為 sizeof(wchar_t)。
);

比較有趣的設計是:如果 lpWideCharStr 傳入的值是 nullptr,而 cchWideChar 是 0 的話,這個 API 的回傳值就會是「該字串完成轉換後所需要的 buffer 大小」。這是因為在不同的編碼下每個「文字」的大小可能不一樣;混用各種文字的字串,經過編碼轉換後,所需要的長度並沒有一定的公式可循,必須得真的轉換過一次,才能得知所需的長度 。

由於考慮到記憶體配置與安全性的問題,一般人在使用這個 API 時,會呼叫兩次:第一次取得所需的 buffer 大小、配置適當的記憶體後,再呼叫第二次,把轉換結果填入 buffer。把整個流程寫成函式的話,會長成這個樣子:

Read More

關於 Windows 中程式參數編碼的一些討論

標題實在下得很爛,但不知道怎麼下比較好。

這篇主要是回答 PTT 上 C_CPP 板,有 Windows programming 的初學者在問 C 程式的命令列為什麼不吃 Unicode 的問題。

這個問題其實是很大的。我的回答可能也不是很清楚。但好歹是我花時間寫的東西,就留在這邊記錄一下吧!


首先,我複議 lwecloud(註:另一位鄉民)的說法:都已經 2023 年,Windows 都是 NT-based 的了,不要再用 MBSC/ANSI 了。

你要做的事,不是把 compiler option 改成 MBSC,而是要想為什麼你在 Unicode 下編譯/執行有問題?

Read More

將只有 ASCII 範圍的 wstring 轉換成 string

Windows 的程式,若是 Unicode enabled 的話,預設使用的編碼是 UTF-16,每個字元的型別為 wchar_t。這樣的字串,無法使用 std::string 處理,必須改用 std::wstring。但有時候,現有的函式如果只吃 std::string,而且我們的字串又只包含有 ASCII 定義的字元(也就是只有英文及半型符號)的話,我們就必須把 std::wstring 轉成 std::string

有一種比較偷懶的做法:

std::wstring ws(L"Hello");
std::string s(ws.begin(), ws.end());

但是在比較嚴謹的 C++ 編譯器(例如 VS 2022),就會發出警告(因為隱式把 wchar_t 轉換成 char,可能造成資料流失)。想要避開警告,就必須明確使用 static_cast<> 進行資料轉換。此時就可以利用 std::transform() 來做:

std::wstring ws(L"Hello");
std::string s(ws.size(), 0);
std::transform(ws.begin(), ws.end(), s.begin(), [](wchar_t c){
    return static_cast<char>c;
});

UTF-8 與 BOM

Unicode 簡介

Unicode(中文翻成「萬國碼」或「統一碼」)是一種文字編碼方式。主要的目的,是希望能提供一套統一的標準,以數字的方式表示全世界人類使用的文字,方便利用電腦、網路儲存及傳輸文字資訊。詳細的歷史沿革,網路上可以找到很多文章,在此不再贅述。

關於碼點 Code Point

每一個被 Unicode 規範定義的文字,都有一個獨一無二的數值,我們稱之為「碼點 (Code Point,亦翻作「代碼點」)」。通常我們會用 U+xxxxxxxx 代表 16 進位的數字)這樣的格式來表示碼點。

Read More