如何在 C++11 中将 std::string 转换为 std::u32string?

Posted

技术标签:

【中文标题】如何在 C++11 中将 std::string 转换为 std::u32string?【英文标题】:How to convert std::string to std::u32string in C++11? 【发布时间】:2020-02-08 13:48:46 【问题描述】:

我正在使用 C++11 中的 Unicode,我现在无法将 std::string 转换为 std::u32string。

我的代码如下:

#include <iostream>
#include <string>
#include <locale>
#include "unicode/unistr.h"
#include "unicode/ustream.h"

int main()

    constexpr char locale_name[] = "";
    setlocale( LC_ALL, locale_name );
    std::locale::global(std::locale(locale_name));
    std::ios_base::sync_with_stdio(false);
    std::wcin.imbue(std::locale());
    std::wcout.imbue(std::locale());

    std::string str="hello☺????";

    std::u32string s(str.begin(),str.end());

    icu::UnicodeString ustr = icu::UnicodeString::fromUTF32(reinterpret_cast<const UChar32 *>(s.c_str()), s.size());
    std::cout << "Unicode string is: " << ustr << std::endl;

    std::cout << "Size of unicode string = " << ustr.countChar32() << std::endl;

    std::cout << "Individual characters of the string are:" << std::endl;
    for(int i=0; i < ustr.countChar32(); i++)
      std::cout << icu::UnicodeString(ustr.char32At(i)) << std::endl;

    return 0;

执行时的输出是:(这不是预期的)

Unicode string is: hello�������
Size of unicode string = 12
Individual characters of the string are:
h
e
l
l
o
�
�
�
�
�
�
�

请建议是否存在为此的任何 ICU 库函数

【问题讨论】:

使用 UTF-32 有什么意义吗? 既然有一个fromUTF32 函数,那么在某个地方也应该有一个toUTF32。这是您将std::string 转换为std::u3string 所需要的。将 std::string 的每个字符复制到 std::u32string 中的每个 unicode 值不会完成任何有用的操作。 你可以在下面的帖子中调整widen函数来做你想做的事:***.com/questions/51210723/how-to-detect-â€-combination-of-unicode-in-c-string/51212415#51212415 ICU 使用 UTF-16 表示。 str 在您的示例中不是 UTF-32 编码的。为什么又要在任一方向使用 UTF-32? str 很可能是 UTF-8 格式,而您想要 UnicodeString::fromUTF8 @dashthird 今天没有人使用 UTF-32。如果在操作系统中,请使用 UTF-16。如果在 Web 中,请使用 UTF-8。您极不可能遇到 BMP 之外的某些字符,因此 UTF-16 就不够用了。 【参考方案1】:

输出是有意义的。大概您认为您正在定义一个包含 7 个字符的字符串?看看str.size()。你定义了一个 12 个字符的字符串!

即使您可以在程序中输入"hello☺?",这个字符串文字也不只包含七个字节。最后两个字符中的每一个都被扩展为多个字节,因为这些字符超出了扩展的 ASCII 范围(0 到 255 或 -128 到 127)。结果是一个 12 字节的字符串文字,它初始化了一个 12 字符的 string,而后者又初始化了一个 12 字符的 u32string。你已经破坏了你想要代表的角色。

示例: 字符 '☺' 表示为三个字节 \0xE2\0x98\0xBA。如果char 在您的系统上签名(很可能),这三个字节的值将是 -30、-104 和 -70。转换为char32_t 会将这些值中的每一个提升为 32 位,然后将有符号转换为无符号,从而产生三个值 429496726642949671924294967226。您可能想要的是将这些字节连接到单个 char32_t\0x00E298BA 中。但是,您的转换不提供(重新)组合字节的机制。

同样,字符'?' 由四个字节\0xF0\0x9F\0x98\0x86 表示。这些被转换为四个 32 位整数,而不是单个值 \0xF09F9886

要获得您想要的结果,您需要告诉编译器将您的字符串文字解释为 7 个字符。尝试以下初始化s

std::u32string s = U"hello☺?";

字符串文字上的U 前缀告诉编译器每个字符代表一个UTF-32 字符。这会产生所需的 7 个字符的字符串(假设您的编译器和编辑器同意字符编码,我认为这很有可能)。


免费调试要点:当你的输出不是你所期望的,检查每个阶段的数据以确保你的输入是你所期望的。

【讨论】:

你说The U prefix on the string literal tells the compiler that each character represents a UTF-32 character.我需要输入字符串怎么办?如果我替换 ``` std::string str="hello☺?"; std::u32string s(str.begin(),str.end());``` 与std::u32string s; std::cin &gt;&gt; s; 我得到错误error: cannot bind ‘std::istream aka std::basic_istream&lt;char&gt;’ lvalue to ‘std::basic_istream&lt;char&gt;&amp;&amp;’ std::cin &gt;&gt; s; @dashthird "如果我需要输入字符串怎么办?" -- 也许问题中应该提到这一点?同样相关的是输入是来自流还是来自 API,因为最好的答案是使用 U 前缀:开始尽可能接近所需的格式。对于控制台输入,请使用std::wcin 和ICU 的wchar_t 支持或std::cin 和u32string conversion from string。【参考方案2】:

感谢大家的帮助!

使用这两个链接,我能够找到一些相关的功能:

https://en.cppreference.com/w/cpp/string/multibyte/mbrtoc32

How to convert a Unicode code point to characters in C++ using ICU?

我尝试使用codecvt 函数,但出现错误:

fatal error: codecvt: No such file or directory
 #include <codecvt>
                   ^
compilation terminated.

所以,我跳过了那个 & 在进一步搜索中,我发现 mbrtoc32() 函数有效:)

这是工作代码:

#include <iostream>
#include <string>
#include <locale>
#include "unicode/unistr.h"
#include "unicode/ustream.h"
#include <cassert>
#include <cwchar>
#include <uchar.h>

int main()

    constexpr char locale_name[] = "";
    setlocale( LC_ALL, locale_name );
    std::locale::global(std::locale(locale_name));
    std::ios_base::sync_with_stdio(false);
    std::wcin.imbue(std::locale());
    std::wcout.imbue(std::locale());

    std::string str;
    std::cin >> str;
    //For example, the input string is "hello☺?"

    std::mbstate_t state; // zero-initialized to initial state
    char32_t c32;
    const char *ptr = str.c_str(), *end = str.c_str() + str.size() + 1;

    icu::UnicodeString ustr;

    while(std::size_t rc = mbrtoc32(&c32, ptr, end - ptr, &state))
    
      icu::UnicodeString temp((UChar32)c32);
      ustr+=temp;
      assert(rc != (std::size_t)-3); // no surrogates in UTF-32
      if(rc == (std::size_t)-1) break;
      if(rc == (std::size_t)-2) break;
      ptr+=rc;
    

    std::cout << "Unicode string is: " << ustr << std::endl;
    std::cout << "Size of unicode string = " << ustr.countChar32() << std::endl;
    std::cout << "Individual characters of the string are:" << std::endl;
    for(int i=0; i < ustr.countChar32(); i++)
      std::cout << icu::UnicodeString(ustr.char32At(i)) << std::endl;

    return 0;

输入hello☺? 的输出与预期一致:

Unicode string is: hello☺?
Size of unicode string = 7
Individual characters of the string are:
h
e
l
l
o
☺
?

【讨论】:

以上是关于如何在 C++11 中将 std::string 转换为 std::u32string?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++17 中将 std::string 转换为 std::vector<std::byte>?

如何在MFC中将std:string转换为LPCTSTR

如何在 C++ 中将整个文件读入 std::string?

如何在 C++ 中将 char 指针附加到 std::string

在 C++ 中将字符串转换为 Cstring

如何在c ++中将值从矢量转换为地图?