2022年10月23日 星期日

C++20 UTF-8 的轉換

 一直都停留在使用 C++17 的標準,其中一個重要原因是 C++20 對 UTF-8 有了一個明確的型別定義,一些舊有的程式都過不了 C++20 這關,所以就一直攔著。

不過後來發現可以用強制型別指示轉換(type&)這招來破解,這招用在其他地方會很危險,因編譯器會無條件通過轉換,不會給你任何警告訊息。

C++20 對於 UTF-8 提供兩個新東西,一個 char8_t 型別,另一個是 std::u8string 類別,也只有這樣而已。最重要的 STL 完全只能用 char 和 std::string。

那麼就只使用 char 和 std::string 不就好了,那是因在 C++20 字串 u8"UTF-8 string" 只能用 char8_t  和  std::u8string 接收。

尤其 std::u8string 要變成 std::string 須用深層複製

std::u8string srcStr = u8"UTF-8 string";
std::string dstStr((const char*)srcStr.c_str(),srcStr.size());

這之間的轉換開銷就大了,這時強制轉換就派上用場了,有幾個方法可用。

// 全以 char 和 std::string 為主
const char *pStr1 = (const char*)u8"UTF-8 string";
std::string Str1(pStr1);
std::string Str2 = (const char*)u8"UTF-8 string";
// 對已存在的 char8_t 和 std::u8string 轉換
const char8_t *pStr3 = u8"UTF-8 string";
std::string Str3((const char*)pStr3);
std::u8string Str4 = u8"UTF-8 string";
std::string &Str5_ref((std::string&)Str4); // 不用深層複製

強制轉換不會造成任何額外的開銷,以下用一個簡單的例子證明這樣做是有效的

// cpp.sh 線上編譯
#include <iostream>

int main()
{
  std::u8string S = u8"Hello World!\n";
  std::string &D = (std::string&)S; // 指向同一個
  D[0] = 'A'; // 把 H 改成 A
  std::cout << (const std::string&)S << std::endl;
}

std::string 和 std::u8string 之間用強制型別指示轉換,之所以不會有問題,是因兩者都用同一個 template class 包裝 char 和 char8_t,而兩者的 char* 和 char8_t* 都會佔有相同的記憶體大小,所以兩者皆可互相用指示取代。






沒有留言:

張貼留言