2016 年 9 月

第 31 卷,第 9 期

本文章是由機器翻譯。

C++ - STL 字串與 Win32 API 的 Unicode 編碼轉換

Giovanni Dicanio

Unicode 是現行的標準,其代表新式軟體中的國際文字。根據官方 Unicode 協會網站 (bit.ly/1Rtdulx),「 Unicode 會提供唯一的數字的每個字元,不論平台為何,無論何種程式,無論何種語言 」。 每個這些唯一的號碼稱為字碼指標,並且通常使用"U +"前置詞,後面接著唯一的數字撰寫十六進位格式來表示。例如,"C"的字元相關聯的字碼指標為 U + 0043。請注意,Unicode 涵蓋世界上書寫系統,包括表意字元大部分的業界標準。因此,比方說,日文漢字表意文字学,具有 「 學習 」 和 「 知識 」 之間的意義,是程式碼點 U + 5B66 相關聯。目前,Unicode 標準會定義多個 1,114,000 字碼指標。

從抽象字碼指標為實際的位元︰ Utf-8 和 utf-16 編碼方式

不過字碼指標是抽象的概念。程式設計人員 」 的問題是︰ 這些 Unicode 字碼指標如何呈現領會電腦位元? 此問題的答案直接通往 Unicode 編碼方式的概念。基本上,Unicode 編碼方式特定且定義完善的代表位元的 Unicode 字碼指標值。Unicode 標準會定義數個編碼,但最重要的是 utf-8 和 utf-16,兩者都是可變長度編碼方式編碼所有可能的 Unicode 」 字元"或,更好、 字碼指標的能力。因此,下列這兩種編碼之間的轉換是不失真格式︰ 任何 Unicode 字元的處理程序將不會遺失。

Utf-8,正如其名,會使用 8 位元程式碼單位。它的設計注意兩項重要特性。首先,它是具有回溯相容性與 ASCII;這表示每個有效的 ASCII 字元程式碼相同的位元組值時使用 utf-8 編碼。換句話說,有效的 ASCII 文字是自動有效 UTF 8 編碼的文字。

第二,因為 Unicode 文字編碼的 utf-8 是採用一系列的 8 位元的位元組單位,無位元組序,但複雜的問題。Utf-8 編碼方式 (不同於 utf-16) 是以位元組由小到大中性的設計。可以有不同的硬體架構與 endianness 不同的電腦的不同電腦系統之間交換文字時,這是一項重要功能。

考慮大寫的字母 C 前面所述的兩個 Unicode 字元編碼 (字碼指標 U + 0043) utf-8 使用單一位元組 0x43 (43 十六進位),也就是完全以字元 C (根據使用 ASCII utf-8 回溯相容性) 相關聯的 ASCII 碼。日文表意文字学 (字碼指標 U + 5B66) 相較之下,utf-8 編碼為三個位元組序列 0xE5 0xAD 0xA6。

Utf-8 是最常用的 Unicode 編碼方式在網際網路上。根據最近 W3Techs 統計資料可在 bit.ly/1UT5EBC, ,87%的就分析的所有網站會使用 utf-8。

Utf-16 很基本上既定標準所使用的編碼 Windows Unicode 的 Api。Utf-16 是 「 原生 」 中許多其他軟體系統,以及編碼的 Unicode。例如,Qt、 Java 和國際元件 Unicode (ICU) 程式庫,只是為了等等,使用 utf-16 編碼來儲存 Unicode 字串。

Utf-16 使用 16 位元程式碼單位。就像 utf-8、 utf-16 可以編碼所有可能的 Unicode 字碼指標。不過,雖然 utf-8 編碼使用一到四個 8 位元的位元組單位,每個有效 Unicode 字碼指標,utf-16 是簡單的方式。事實上,以 utf-16 使用只有一個或兩個 16 位元程式碼單位編碼的 Unicode 字碼指標。不過,具有大於單一位元組字碼單位表示位元組序,但複雜︰ 事實上,帶來的同時位元組由大到小 utf-16 位元組由小到大 utf-16 (雖然只有一個位元組由小到大中性 utf-8 編碼)。

Unicode 定義平面的概念為 65536 (2 的連續群組16) 字碼指標。第一個平面識別為平面 0 或基本多語系平面 (BMP)。幾乎所有現代化的語言和許多符號的字元在 BMP,且所有這些 BMP 字元會以 utf-16 使用單一的 16 位元程式碼單位表示。

增補字元位於平面以外 BMP。其中包括 pictographic 符號,例如 Emoji 和埃及 hieroglyphs 像歷史指令碼。外部 BMP 這些補充字元會以 utf-16 使用兩個 16 位元程式碼單位,也稱為 surrogate 字組編碼。

大寫的字母 C (U + 0043) 以單一的 16 位元程式碼單元 0x0043 以 utf-16 編碼。表意文字学 (U + 5B66) 會以 utf-16 編碼,為單一的 16 位元程式碼單元 0x5B66。對於許多 Unicode 字元的即時、 直接與之間的對應其程式碼點 「 抽象 」 表示法 (例如,U + 5B66) 及其相關聯 utf-16 編碼十六進位 (比方說,0x5B66 16 位元單字)。

有一些有趣,我們先看看一些 pictographic 符號。Unicode 字元"雪人 」 ( U+2603 U + 2603年) 以三個位元組序列的 utf-8 編碼︰ 0xE2 0x98 0x83;不過,它的 utf-16 編碼方式是單一的 16 位元單位 0x2603。Unicode 字元 「 啤酒咖啡杯 」 ( U+1F37A U + 1F37A),由四個位元組順序 0xF0 0x9F 所在 BMP、 外部 utf-8 編碼 0x8D 0xBA。它的 utf-16 編碼方式改為使用兩個 16 位元程式碼單位,0xD83C 0xDF7A,utf-16 surrogate 字組的範例。

Utf-8 和 utf-16 使用 Win32 Api 之間轉換

如先前段落中所述,使用不同的位元,根據特定的 Unicode 編碼方式的電腦記憶體內表示 Unicode 文字。哪一種編碼您應該使用? 沒有單一的回應,這個問題。

最近,普遍似乎較理想的方法組成儲存跨平台 c + + 程式碼中的 std:: string 類別的執行個體中的 utf-8 編碼的 Unicode 文字。此外,還有一般協議,utf-8 是跨應用程式界限,並在不同機器之間交換文字所選擇的編碼方式。Utf-8 是中性位元組由小到大的格式會在此扮演重要角色。在任何情況下,utf-8 和 utf-16 之間的轉換所需最少的 Win32 API 界限,因為 Windows unicode API 會使用 utf-16 做為其原生編碼。

現在讓我們深入探討一些 c + + 程式碼,來實作這些 Unicode UTF-8-8/utf-16 編碼方式轉換。有兩個金鑰可用於此用途的 Win32 Api: MultiByteToWideChar 和其對稱 WideCharToMultiByte。前者可以叫用從 utf-8 (在特定的 API 術語 「 多位元組 」 字串) 轉換成 utf-16 (「 寬字元 」 字串)。後者可用於相反。由於這些 Win32 函式有相似的介面和使用模式,我將著重在本文中,MultiByteToWideChar 只,但包含本文的可下載的一部分使用其他 API 的 c + + 可編譯程式碼。

使用標準 STL 字串類別來儲存 Unicode 文字 因為這是 c + + 文件時,會以某種形式的字串類別儲存 Unicode 文字有效期望。因此,現在的問題就變成︰ 何種 c + + 字串類別可以用來儲存 Unicode 文字? 答案根據 Unicode 文字所使用的特定編碼。如果使用 utf-8 編碼方式,因為它以 8 位元程式碼單位,簡單的 char 可用來代表每個 c + + 中的這些程式碼單位。在此情況下 STL std:: string 類別,是以字元為基礎,是個不錯的選項來儲存 UTF 8 編碼的 Unicode 文字。

相反地,若以 utf-16 編碼的 Unicode 文字時,每個程式碼單元會以 16 位元字組。在 Visual c + +,wchar_t 類型是完全 16 位元的大小。因此,STL std:: wstring 類別,是 wchar_t 為基礎,來儲存 utf-16 Unicode 文字運作正常。

值得注意的是 c + + 標準不指定大小的 wchar_t 類型,讓它等於使用 Visual c + + 編譯器的 16 位元,而其他的 c + + 編譯器可以隨意使用不同的大小。而且事實上,wchar_t GNU GCC c + + 編譯器在 Linux 上所定義的大小為 32 位元。Wchar_t 類型具有不同的編譯器和平台上的大小不同,因為 std:: wstring 類別中,會根據該型別,是不可移植。換句話說,wstring 可以用來儲存使用 Visual c + + 編譯器 (wchar_t 大小是 16 位元),以在 Windows 上的 utf-16 編碼的 Unicode 文字,但不是在 Linux 上使用 GCC c + + 編譯器,以定義不同大小的 32 位元 wchar_t 類型。

沒有實際上的另一個 Unicode 編碼,也就是較不知名並用小於實際上比其同層級項目︰ UTF-32。正如其名稱清楚地,它為基礎的 32 位元字碼單位。因此 GCC/Linux 32 位元 wchar_t 是 Linux 平台上的 utf-32 編碼的理想候選。

Wchar_t 大小模稜兩可決定後續缺乏的功能 (包括 std:: wstring 類別本身) 為基礎的 c + + 程式碼可攜性。相反地,std:: string,也就是以字元為基礎,是可攜式的。不過,實際的觀點而言,值得注意的是使用 wstring 儲存 utf-16 編碼的文字 Windows 特定 c + + 程式碼中沒有問題。事實上,程式碼中的這些部分已經互動的 Win32 Api,當然,平台特定的定義。因此將 wstring 加入至混合,不會變更這種情況。

最後,務必要注意,因為 utf-8 和 utf-16 可變長度編碼方式,string:: length 和 wstring::length 方法的傳回值通常沒有對應的 Unicode 字元 (或字碼指標) 儲存在字串數目。

轉換函式介面 我們開發來轉換為對等使用 utf-16 編碼的文字以 utf-8 編碼的 Unicode 文字的函式。這會很有用,比方說,當有一些儲存 UTF 8 編碼的 Unicode 字串的跨平台 c + + 程式碼使用 STL std:: string 類別,而您想要將該文字至 Unicode 的 Win32 Api,這通常會使用 utf-16 編碼方式。此程式碼進行通訊的 Win32 api,因為它已經是不可移植以便 std:: wstring 非常適合用來儲存 utf-16 文字。可能有函式原型是︰

std::wstring Utf8ToUtf16(const std::string& utf8);

此轉換函數會做為 Unicode UTF 8 編碼的輸入字串,其中會儲存在標準的 STL std:: string 類別。這是輸入的參數,因為它傳址方式傳遞 const (const &) 函式。轉換的結果則會傳回以 utf-16 編碼的字串,std:: wstring 執行個體中儲存。不過,在 Unicode 編碼方式轉換,過程中都有可能發生錯誤。例如,輸入的 utf-8 字串可能包含無效的 utf-8 序列 (可以是錯誤的程式碼中,其他部分的結果,或它最後可能會那里一些惡意活動的結果)。在這種情況下,最佳作法是從安全性角度來看是無法轉換,而不是使用有潛在危險的位元組序列。轉換函式可以處理無效 utf-8 輸入序列的情況的下擲回 c + + 例外狀況。

定義的轉換錯誤的例外狀況類別 什麼類型的 c + + 類別可用於 Unicode 編碼轉換失敗時擲回例外狀況? 選項可能會使用類別中已定義的標準程式庫,例如︰ std::runtime_error。不過,我想定義一個新的自訂 c + + 例外狀況類別來達成目的,它衍生自 std::runtime_error。在 Win32 Api,像是 MultiByteToWideChar 失敗時,就可以呼叫 GetLastError 來取得進一步的失敗原因的相關資訊。例如,如果輸入字串中的無效 utf-8 序列,GetLastErorr 所傳回的常見的錯誤程式碼是 ERROR_NO_UNICODE_TRANSLATION。必須將這項資訊新增至自訂的 c + + 例外狀況類別。它會非常方便稍後進行偵錯。這個例外狀況類別的定義可以啟動像這樣︰

// utf8except.h
#pragma once
#include <stdint.h>   // for uint32_t
#include <stdexcept>  // for std::runtime_error
// Represents an error during UTF-8 encoding conversions
class Utf8ConversionException
  : public std::runtime_error
{
  // Error code from GetLastError()
  uint32_t _errorCode;

請注意,GetLastError 所傳回的值是 DWORD,代表 32 位元不帶正負號的整數型別。不過,DWORD 是 Win32 特定不可移植 typedef。即使從 Win32 特定部份的 c + + 程式碼擲回這個 c + + 例外狀況類別,它可以攔截跨平台 c + + 程式碼 ! 因此,它就懂使用可攜式 typedef,而不是 Win32 特定項目;uint32_t 是這種類型的範例。

接下來,您可以定義一個建構函式,以初始化與錯誤訊息和錯誤碼這個自訂的例外狀況類別的執行個體︰

public:
  Utf8ConversionException(
    const char* message,
    uint32_t errorCode
  )
    : std::runtime_error(message)
    , _errorCode(errorCode)
  { }

最後,可以定義公用 getter 提供唯讀存取的錯誤程式碼︰

uint32_t ErrorCode() const
  {
    return _errorCode;
  }}; // Exception class

因為這個類別衍生自 std::runtime_error,就可以呼叫的內容傳入建構函式的方法,以取得錯誤訊息。請注意,在此類別定義中,只可移植的標準項目已使用,所以這個類別是完全可消耗的 c + + 的跨平台部分程式碼,即使這些位於遠處從 Windows 特定擲回點。

將 utf-8 轉換成 utf-16: MultiByteToWideChar 作用中

現在已定義的轉換函式原型和自訂的 c + + 例外狀況類別實作來適當表示 utf-8 轉換失敗,它可以開始開發的轉換函式主體。為已預期,utf-16 從 utf-8 格式的轉換工作可使用 MultiByteToWideChar Win32 API。詞彙 「 多位元組 」 和 「 寬字元 」 的歷史原因根項目。基本上,此 API,而且其對稱的同層級 WideCharToMultiByte 是一開始是用來儲存在特定字碼頁和 Unicode 文字,會使用 Win32 unicode Api 中的 utf-16 編碼的文字之間進行轉換。寬字元是指 wchar_t,因此它有相關聯的為 wchar_t 為基礎的字串,也就是 UTF 16 編碼的字串。相反地,多位元組字串是一連串的位元組來表示字碼頁中。舊版的字碼頁概念然後已擴充包含 utf-8 編碼方式。

此 API 的一般使用模式包含第一個呼叫 MultiByteToWideChar,以取得結果字串的大小。然後,根據該大小值配置某些字串緩衝區。這通常是使用 std::wstring::resize 方法,如果目的地是 utf-16 字串。(如需詳細資訊,可能會想要閱讀我 2015 年 7 月的文章,「 使用 STL 字串在 Win32 API 界限 」 在 msdn.com/magazine/mt238407。) 最後,使用目的地 MultiByteToWideChar 函式被叫用第二次執行實際的編碼方式轉換的字串先前配置的緩衝區。請注意,相同的使用模式套用對稱 WideCharToMultiByte api。

讓我們在 c + + 程式碼,自訂 Utf8ToUtf16 轉換函式的主體內實作此模式。開始處理特殊案例中是空的輸入字串,其中傳回的只是空的輸出 wstring:

#include <Windows.h> // For Win32 APIs
#include <string>    // For std::string and std::wstring
std::wstring Utf8ToUtf16(const std::string& utf8)
{
  std::wstring utf16; // Result
  if (utf8.empty())
  {
    return utf16;
  }

轉換旗標 MultiByteToWideChar 可以呼叫第一次取得目的地 utf-16 字串的大小。此 Win32 函式相當複雜的介面,而且其行為根據一些旗標所定義。因為 Utf8ToUtf16 轉換函式主體中兩次,就會呼叫此 API,是不錯的作法,程式碼的可讀性和可維護性來定義具名的常數可以用兩次呼叫︰

// Safely fails if an invalid UTF-8 character
// is encountered in the input string
constexpr DWORD kFlags = MB_ERR_INVALID_CHARS;

它也是很好的作法,從安全性角度來看,讓轉換程序,如果輸入字串中發現無效的 utf-8 序列失敗。使用 MB_ERR_INVALID_CHARS 旗標也鼓勵 Michael Howard 與 David LeBlanc 活頁簿中,「 撰寫安全程式碼,Second Edition 》 (Microsoft Press,2003年)。

如果您的專案使用較舊的 Visual c + + 編譯器版本不支援 constexpr 關鍵字,您可以取代靜態內容中的常數。

字串長度,以及安全轉換為 int size_t MultiByteToWideChar 預期而 STL 字串類別的長度方法會傳回類型的值等於 size_t 使用 int 型別表示的輸入的字串長度參數。在 64 位元組建中,Visual c + + 編譯器會發出警告信號可能遺失資料,從 size_t (其大小為 8 個位元組) 轉換為 int (這是 4 個位元組的大小)。但即使是在 32 位元的組建中,其中 size_t 和 int 被定義為 32 位元整數,Visual c + + 編譯器,因為的不帶正負號/簽章不相符︰ size_t 是不帶正負號,而簽署 int。這不是合理的長度的字串,但巨大字串的長度大於 (2 的問題31-1) — 也就是超過 2 億個位元組的大小 — 從帶正負號的整數 (size_t) 轉換為帶正負號的整數 (int) 可以產生負數時,並不合理負數長度。

因此,而不是只叫用 utf8.length 取得來源 utf-8 輸入字串的大小,然後傳遞給 MultiByteToWideChar API,最好檢查長度的實際 size_t 值,並確定它可以安全且有意義地加以轉換成整數,則只將它傳遞給 MultiByteToWideChar API。

下列程式碼可以用來確定該 size_t 長度不超過變數型別為 int,它會擲回例外狀況的最大值︰

if (utf8.length() > static_cast<size_t>(std::numeric_limits<int>::max()))
{
  throw std::overflow_error(
    "Input string too long: size_t-length doesn't fit into int.");
}

請注意 std::numeric_limits 類別樣板 (來自 < 限制 > c + + 標準標頭中) 使用查詢最大的可能值為 int 類型。不過,此程式碼可能實際上無法編譯。如何會這樣呢? 問題在於定義中的最小和最大 Windows Platform SDK 標頭中的巨集。特別是,Windows 特定的定義最大的前置處理器巨集衝突 std::numeric_limits < int >:: 最大成員函式呼叫。有一些方法來避免這種情形。

可能的解決方案是以 #define NOMINMAX 之前 < windows.h >。這將導致 min 和 max Windows 特定的前置處理器巨集的定義。不過,防止這些巨集的定義實際上可能會導致問題的其他 Windows 標頭,例如 < gdiplus.h >,需要這些 Windows 特定巨集的定義。

因此,另一個選項是使用要想避免上述的巨集展開的額外組 std::numeric_limits::max 成員函式呼叫前後的括號︰

if (utf8.length() > static_cast<size_t>((std::numeric_limits<int>::max)()))
{
  throw std::overflow_error(
    "Input string too long: size_t-length doesn't fit into int.");
}

此外,或者,可以使用 INT_MAX 常數,而不是 c + + std::numeric_limits 類別範本。

使用任何方法,完成大小檢查和長度值找不到都適用的 int 型別變數,從 size_t 轉換成 int 可以安全地執行使用 static_cast:

// Safely convert from size_t (STL string's length)
// to int (for Win32 APIs)
const int utf8Length = static_cast<int>(utf8.length());

請注意 utf-8 字串的長度以 8 位元的字元為單位;也就是說,在位元組。

第一個 API 呼叫︰ 取得目標字串的長度 現在 MultiByteToWideChar 可以呼叫第一次,以取得目的 utf-16 字串的長度︰

const int utf16Length = ::MultiByteToWideChar(
  CP_UTF8,       // Source string is in UTF-8
  kFlags,        // Conversion flags
  utf8.data(),   // Source UTF-8 string pointer
  utf8Length,    // Length of the source UTF-8 string, in chars
  nullptr,       // Unused - no conversion done in this step
  0              // Request size of destination buffer, in wchar_ts
);

請注意如何叫用函式傳遞零做為最後一個引數。這會指示 MultiByteToWideChar API 只傳回目的地字串; 所需的大小在此步驟中不進行任何轉換。請注意,目的地字串的大小以 wchar_ts (不在 8 位元字元為單位),這很合理,因為目的地字串 UTF 16 編碼的 Unicode 字串,由 16 位元 wchar_ts 的序列表示。

若要取得輸入的 utf-8 std:: string 的內容的唯讀存取,稱為 std::string::data 方法。因為 utf-8 字串的長度會明確地傳遞做為輸入參數,此程式碼將用於也具有內嵌 NULs 內的 std:: string 的執行個體。

也請注意 CP_UTF8 常數來指定輸入的字串以 utf-8 編碼的使用。

處理錯誤情況 如果上述函式呼叫失敗,例如,在出現在輸入字串中,無效的 utf-8 序列 MultiByteToWideChar API 會傳回零。在此情況下,GetLastError Win32 函式可以叫用來取得進一步的詳細資料失敗的原因。發生無效的 utf-8 字元時傳回的常見的錯誤程式碼是 ERROR_NO_UNICODE_TRANSLATION。

因應系統故障,就會擲回例外狀況。這可能是先前的自訂設計 Utf8ConversionException 類別的執行個體︰

if (utf16Length == 0)
{
  // Conversion error: capture error code and throw
  const DWORD error = ::GetLastError();
  throw Utf8ConversionException(
    "Cannot get result string length when converting " \
    "from UTF-8 to UTF-16 (MultiByteToWideChar failed).",
    error);
}

配置的記憶體,目的地字串 Win32 函式呼叫成功,如果需要的目的地字串長度儲存在 utf16Length 本機變數,因此目的地記憶體可以配置輸出 utf-16 字串。Std:: wstring 類別的執行個體中儲存的 utf-16 字串,請調整方法的簡單呼叫都會順利運作︰

utf16.resize(utf16Length);

請注意,因為輸入 utf-8 字串的長度已明確傳遞至 MultiByteToWideChar (而不是只傳遞為-1,並詢問要掃描整個輸入的字串,直到找到 NUL 結束字元的 API),Win32 API 都不會將其他 NUL 結束字元加入至產生的字串︰ API 只會處理的確切明確傳遞的長度值所指定的輸入字串中的字元數目。因此,就不需要叫用與 std::wstring::resize"utf16Length + 1 」 值︰ 因為沒有其他 NUL 結束字元將 scribbled 在 Win32 api 中,您不必挪出空間,在目的地 std:: wstring (更多詳細資料,可以在 2015 年 7 月篇文章中找到)。

第二個 API 呼叫︰ 進行實際轉換 既然 utf-16 wstring 執行個體有足夠的空間來裝載產生 utf-16 編碼的文字,終於呼叫 MultiByteToWideChar 第二次,以取得實際的已轉換位元目標字串中︰

// Convert from UTF-8 to UTF-16
int result = ::MultiByteToWideChar(
  CP_UTF8,       // Source string is in UTF-8
  kFlags,        // Conversion flags
  utf8.data(),   // Source UTF-8 string pointer
  utf8Length,    // Length of source UTF-8 string, in chars
  &utf16[0],     // Pointer to destination buffer
  utf16Length    // Size of destination buffer, in wchar_ts          
);

請注意使用 」 (& s) utf16 [0]"語法,以取得寫入存取權 std:: wstring 的內部記憶體緩衝區 (,,已經討論在 2015 年 7 月文章)。

如果 MultiByteToWideChar 第一次呼叫成功,則不太可能,此第二個呼叫會失敗。儘管如此,檢查 API 傳回值是當然不錯、 安全的程式撰寫作法︰

if (result == 0)
{
  // Conversion error: capture error code and throw
  const DWORD error = ::GetLastError();
  throw Utf8ConversionException(
    "Cannot convert from UTF-8 to UTF-16 "\
    "(MultiByteToWideChar failed).",
    error);
}

否則,如果成功,產生的 utf-16 字串可能最後會傳回給呼叫者︰

 

return utf16;
} // End of Utf8ToUtf16

使用方式範例 因此,如果您有 UTF 8 編碼的 Unicode 字串 (例如,來自一些 c + + 跨平台程式碼),而且您想要將它傳遞給 Win32 unicode 的 API,這個自訂的轉換函式可以叫用就像這樣︰

std::string utf8Text = /* ...some UTF-8 Unicode text ... */;
// Convert from UTF-8 to UTF-16 at the Win32 API boundary
::SetWindowText(myWindow, Utf8ToUtf16(utf8Text).c_str());
// Note: In Unicode builds (Visual Studio default) SetWindowText
// is expanded to SetWindowTextW

Utf8ToUtf16 函式會傳回包含 UTF 16 編碼字串的 wstring 執行個體,以前往 NUL 結束字串,傳遞至 Win32 unicode Api 的 C 樣式的原始指標的這個執行個體上叫用 c_str 方法。

反向轉換從 utf-16 為 utf-8,這次呼叫 WideCharToMultiByte API 可以寫入非常類似的程式碼。如先前所述,都不失真之間 utf-8 和 utf-16 Unicode 轉換,轉換程序期間,任何字元將會遺失。

Unicode 編碼方式轉換程式庫

可編譯 c + + 程式碼範例會包含在可下載的封存與本文相關聯。這是可重複使用程式碼,在 Visual c + + 警告層級 4 清楚編譯 (/ /w4) 在 32 位元和 64 位元的組建。它被實作為標頭僅限 c + + 程式庫。基本上,此 Unicode 編碼方式轉換模組包含兩個標頭檔︰ utf8except.h 和 utf8conv.h。前者包含用來表示的 Unicode 編碼轉換期間的錯誤狀況的 c + + 例外狀況類別的定義。後者會實作實際的 Unicode 編碼方式轉換函式。

請注意該 utf8except.h 包含僅跨平台 c + + 程式碼,因此能夠攔截 c + + 專案,包括不是 Windows 特有的並改為使用跨平台 c + + 所設計的程式碼部分中的任何位置的 utf-8 編碼轉換例外狀況。相反地,utf8conv.h 包含 Windows 專有的 c + + 程式碼,因為它與 Win32 API 界限的直接互動。

若要重複使用此程式碼專案中的,只是 #include 先前提及的標頭檔。可下載的封存檔包含實作一些測試案例,以及其他原始程式檔。

總結

Unicode 是現行的標準,其代表新式軟體中的國際文字。可以以各種格式編碼的 Unicode 文字︰ 兩個最重要的是 utf-8 和 utf-16。在 c + + Windows 程式碼很多 utf-8 和 utf-16 之間進行轉換的需求,因為 unicode Win32 Api utf-16 做為其原生 Unicode 編碼方式。Std:: wstring 適合將 UTF 16 編碼的文字儲存在以 Visual c + + 編譯器為目標的 Windows c + + 程式碼時,utf-8 文字,可以方便地儲存在 STL std:: string 類別的執行個體。

Win32 Api MultiByteToWideChar 和 WideCharToMultiByte 可用來執行使用 utf-8 和 utf-16 編碼表示的 Unicode 文字之間的轉換。我會示範將它包裝在可重複使用的現代 c + + helper 函數來從 utf-8 轉換成 utf-16 MultiByteToWideChar API 的使用模式的詳細的說明。反向轉換會遵循非常類似的模式,並實作它的可重複使用 c + + 程式碼可用於本文章的下載。


Giovanni Dicanio 是電腦程式設計人員專精於 c + + 和 Windows、 Pluralsight 作者和 Visual c + + MVP。 他喜歡除了程式設計和撰寫課程,幫助其他人在論壇和社群專用於 c + +,而且可以存取 giovanni.dicanio@gmail.com。他也撰寫的部落格 blogs.msmvps.com/gdicanio

感謝閱本篇文章的下列技術專家︰ David Cravey 和 Marc Gregoire
David Cravey GlobalSCAPE 企業架構,會導致數個 c + + 使用者的群組,但四個階段 Visual c + + MVP。

Marc Gregoire 從比利時、 比利時 c + + 使用者的群組,這作者"Professional c + + 」 (Wiley),「 c + + 標準程式庫快速參考 」 (Apress),技術編輯共同作者的書籍,而由於 2007年的創辦人的資深軟體工程師,收到每年的 MVP 獎勵的他 VC + + 專業知識。連絡 Marc marc.gregoire@nuonsoft.com