【问题标题】:How to convert between BSTR and 32-bit Unicode strings in Visual C++?如何在 Visual C++ 中的 BSTR 和 32 位 Unicode 字符串之间进行转换?
【发布时间】:2017-08-31 13:48:35
【问题描述】:

我有 punycodes 字符串(转义和非转义)的第 3 方代码。作为 Unicode 输入/输出,它使用 32 位 Unicode 字符串(基于 uint32_t),而不是 16 位。我自己的输入/输出是 BSTR(UTF 16 位)。我应该如何在 32 位 Unicode 字符数组和 BSTR(双向)之间进行转换?

代码应在 Visual C++ 6.0 及更高版本中运行。

【问题讨论】:

  • 你需要一个第三方库,<codecvt> header 直到 VS2010 才可用
  • 您不能为该语言的过时 版本寻求解决方案。 VC 6 已经 20 岁了。那时 C++ 甚至不支持 Unicode。当前的 language 版本是 C++ 17。该语言在 C++ 11 中通过 char16_tchar32_t 获得了 Unicode 支持和文字。 C++ 14 在 STL 中添加了对 u16string、u32string 的支持。没有任何符合标准的 解决方案可以与 20 年前的编译器一起使用。检查String and Character literals以供参考
  • @Mgetz 这不仅仅是何时添加codecvt 的问题。毕竟BSTR是一个有长度的UTF16字符串。 20 年前,该语言本身没有 Unicode 支持。现在有了。 uint32_t 是现在要使用的 错误 类型。而且我真的不认为 20 年前 VC++ 6 能够可靠地与 UTF32 一起工作。字符集转换委托给操作系统。我认为 VC++ 6 当时没有对 UTF32 进行适当的调用,只是因为它们不存在
  • @Mgetz,不,它在 C++11 中获得了 Unicode 字符串、文字等。它确实现在有char16_tchar32_tu16stringu32string。检查String and Character Literals。它仍然没有的是 UTF8 支持。 charstring和good will用于UTF8,希望other开发者不要使用错误的codepage来读取本地化文件
  • @Alex:最好的选择是使用第三方 Unicode 库,例如 libiconvICU。但是 UTF16UTF32 之间的转换对于手动实现来说非常简单,即使在旧的 C++ 版本中也是如此。手动将 UTF-16 字符串转换为 UTF-32 字符串并不难,反之亦然。

标签: c++ unicode bstr


【解决方案1】:

对于小于0xFFFF 的字符,UTF16 与 UTF32 相同。您可以使用以下转换在 Windows 中显示 UTF-32 代码。

注意,这是基于维基百科UTF16 文章。我没有添加任何错误检查,它需要有效的代码。

void get_utf16(std::wstring &str, int ch32)
{
    const int mask = (1 << 10) - 1;
    if(ch32 < 0xFFFF)
    {
        str.push_back((wchar_t)ch32);
    }
    else
    {
        ch32 -= 0x10000;
        int hi = (ch32 >> 10) & mask;
        int lo = ch32 & mask;

        hi += 0xD800;
        lo += 0xDC00;

        str.push_back((wchar_t)hi);
        str.push_back((wchar_t)lo);
    }
}

例如,以下代码应在 Windows 10 中显示笑脸:

std::wstring str;
get_utf16(str, 0x1f600);
::MessageBoxW(0, str.c_str(), 0, 0);


编辑:

从 UTF-32 码位数组中获取 UTF-16,以及反向操作:

UTF-16 字符串可以是一个 wchar_t 字符长(每个代码点 2 个字节),或 2 个 wchar_t 字符连接在一起(每个代码点 4 个字节)。如果第一个字符介于 0xD8000xE000 之间,则表示每个代码点 4 个字节。

bool get_str_utf16(std::wstring &dst, const std::vector<unsigned int> &src)
{
    const int mask = (1 << 10) - 1;
    for(size_t i = 0; i < src.size(); i++)
    {
        unsigned int ch32 = src[i];
        ////check for invalid range
        //if(ch32 > 0x10FFFF || (ch32 >= 0xD800 && ch32 < 0xE000))
        //{
        //  cout << "invalid code point\n";
        //  return false;
        //}

        if(ch32 > 0x10000)
        {
            ch32 -= 0x10000;
            int hi = (ch32 >> 10) & mask;
            int lo = ch32 & mask;
            hi += 0xD800;
            lo += 0xDC00;
            dst.push_back((wchar_t)hi);
            dst.push_back((wchar_t)lo);
        }
        else
        {
            dst.push_back((wchar_t)ch32);
        }
    }
    return true;
}

void get_str_utf32(std::vector<unsigned int> &dst, const std::wstring &src)
{
    for(unsigned i = 0; i < src.size(); i++)
    {
        const wchar_t ch = src[i];
        if(ch >= 0xD800 && ch < 0xE000)
        {
            //this character is joined with the next character
            if(i < src.size() - 1)
            {
                unsigned int hi = src[i]; i++;
                unsigned int lo = src[i];
                hi -= 0xD800;
                lo -= 0xDC00;
                unsigned int u32 = 0x10000 + (hi << 10) + lo;
                dst.push_back(u32);
            }
        }
        else
        {
            dst.push_back(ch);
        }
    }
}

例子:

std::wstring u16 = L"123?456";

std::vector<unsigned int> u32;
get_str_utf32(u32, u16);
cout << "\n";

cout << "UTF-32 result: ";
for(auto e : u32)
    printf("0x%X ", e);
cout << "\n";

std::wstring test;
get_str_utf16(test, u32);
MessageBox(0, test.c_str(), (u16 == test) ? L"OK" : L"ERROR", 0);

【讨论】:

  • 非常感谢!现在我需要弄清楚如何将 16 位 BSTR 转换为 32 位(反向转换)。希望通过您提供的链接,实现算法应该相当容易。
  • 太棒了。不客气。确保进行一些酷刑测试。我在我的应用程序中使用了上述代码的第一部分,它似乎很稳定。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-14
  • 1970-01-01
  • 2019-08-23
  • 1970-01-01
  • 1970-01-01
  • 2011-10-11
  • 2021-01-31
相关资源
最近更新 更多