【问题标题】:Unicode and std::string in C++C++ 中的 Unicode 和 std::string
【发布时间】:2011-05-02 09:23:41
【问题描述】:

如果我用 C++ 向文件写入包含一些 unicode 字符的随机字符串,我的文本编辑器会告诉我我没有创建有效的 UTF-8 文件。

// Code example
const std::string charset = "abcdefgàèíüŷÀ";
file << random_string(charset); // using std::fstream

我能做些什么来解决这个问题?我需要做很多额外的手动编码吗?我理解它的方式,std::string 不关心编码,只关心字节,所以当我传递一个 unicode 字符串并将其写入文件时,该文件肯定应该包含相同的字节并被识别为 UTF- 8个编码文件?

【问题讨论】:

  • 你在看std::wstring吗?
  • 只是一个疯狂的猜测:可能是您的 random_string 函数由于 charset 字符串的一对一错误而意外插入了空值?
  • @Charles: 这就像我 :) 但我对此表示怀疑,因为 std::string 构造函数会丢弃字符串文字中的空值,而 random_string 函数只是从字符集字符串。
  • std::string 不一定会丢弃字符串文字中的空值。通常,它在内部将字符串表示为以 null 结尾的 C 字符串,以便轻松实现 std::string::c_str() 函数。
  • 字符:'àèíüŷÀ' 不是 UTF-8。注意 UTF-8 是一个多字节字符集。这意味着 charset[x] 不能保证得到一个完整的字符,因为它可能会被拆分为多个字符。

标签: c++ string file unicode


【解决方案1】:

random_string 很可能是罪魁祸首;我想知道它是如何实现的。如果您的字符串确实是 UTF-8 编码的并且 random_string 看起来像

std::string random_string(std::string const &charset)
{
    const int N = 10;
    std::string result(N);
    for (int i=0; i<N; i++)
        result[i] = charset[rand() % charset.size()];
    return result;
}

然后它将从charset 中随机抽取chars,在UTF-8 中(正如其他海报指出的那样)不是Unicode 代码点,而是简单的字节。如果它从 UTF-8 多字节字符的中间选择一个随机字节作为第一个字节(或将其放在 7 位 ASCII 兼容字符之后),那么您的输出将不是有效的 UTF-8。见WikipediaRFC 3629

解决方案可能是transformrandom_string 中的UTF-32 之间的关系。我相信 wchar_tstd::wstring 在 Linux 上使用 UTF-32。只要您留在Basic Multilingual Plane 内,UTF-16 也是安全的。

【讨论】:

  • 所以如果一个名为“str”的std::string包含“àỳ”,str[0]不会返回“à”?而且 str[1] 不会返回“ỳ”?
  • 不,它将返回这些字符的多字节编码中的第一个字节。 C++ 是 1980 年代的发明,旨在与 C(1970 年代)和 ASCII(1960 年代)兼容,而 Unicode 和 UTF-8 是在 90 年代初引入的。 UTF-8 旨在让 大多数 旧程序和算法保持正常工作,看起来您使用了其中一种破坏算法。 如果这或多或少是random_string 所做的。
  • 是的。我想这意味着每当我想操作 unicode 字符串时,我必须使用 wstring。我将阅读可移植性问题等。无论如何,答案已接受。
  • 更正我之前的评论:str[1] 将返回à 编码中的第二个字节。
  • 使用 UTF-8 和 wstring 解决问题有什么问题吗?我必须转换为 UTF-32(或 UTF-16)的任何特殊原因?
【解决方案2】:

我能做些什么来解决这个问题?我有吗 做很多额外的手册 编码?按照我的理解, std::string 不关心 编码,只有字节,所以当我 传递一个 unicode 字符串并写入 归档,当然该文件应该 包含相同的字节并且是 识别为 UTF-8 编码文件?

std::string 与编码无关,这是正确的。它只包含一个char 元素的数组。这些char 元素如何解释为文本取决于环境。如果您的语言环境未设置为某种形式的 Unicode(即 UTF-8 或 UTF-16),那么当您输出字符串时,它将不会显示/解释为 Unicode。

您确定您的字符串文字“abcdefgàèíüŷÀ”是实际上 Unicode 而不是Latin-1? (ISO-8859-1 或可能的 Windows-1252)?您需要确定您的平台当前配置使用的语言环境。

------------编辑-----------

我想我知道您的问题:charset 字符串文字中的一些 Unicode 字符,例如重音字符“À”,是 双字节 字符(假设为 UTF-8 编码)。当您在 random_string 函数中使用 [] 运算符对字符集字符串进行寻址时,您将返回 half 个 Unicode 字符。因此random-string 函数会创建一个无效的字符串。

例如,考虑以下代码:

std::string s = "À";
std::cout << s.length() << std::endl;

在字符串文字被解释为 UTF-8 的环境中,此程序将输出 2。因此,字符串的第一个字符 (s[0]) 只是 Unicode 字符的 一半,因此无效。由于您的 random_string 函数使用 [] 运算符按单个字节寻址字符串,因此您正在创建无效的随机字符串。

是的,您需要使用std::wstring,并使用L 前缀创建您的字符集字符串文字。

【讨论】:

  • 这可能是问题所在,因为我之前能够将 unicode 字符串从文件(以 UTF-8 编码)读取到 std::string 并将其输出到不同的文件。我会调查的。
  • 是的,我想就是这样。看我的回答。
  • 这正是我说你不能在std::string 中存储多字节编码的原因。但由于某种原因,我被否决了。
  • @Let_Me_Be,因为您可以将多字节编码存储在std::string 中。我只是在上面的例子中这样做了。您根本无法使用 [] 运算符处理字符串的单个多字节字符。
  • @Charles 是的,就像我可以使用链表进行随机访问一样。
【解决方案3】:

在您的代码示例中,std::string charset 存储您编写的内容。也就是说,如果您使用 UTF-8 文本编辑器编写此代码,那么您在文件输出中收到的内容将正是该 UTF-8 文本。

UTF-8 只是一种编码方案,其中不同的字符使用不同的字节大小。但是,如果您使用 UTF-8 编辑器,它会编码,比如 'ñ' 有两个字节,,当你将它写入文件时,它将有两个字节(再次是 UTF- 8 兼容)。

问题可能是您用于创建源 C++ 文件的编辑器。它可能使用 latin1 或其他一些编码。

【讨论】:

    【解决方案4】:

    要编写 UTF-8,您需要使用像 this one 这样的 codecvt facet。如何使用它的例子可以看here

    【讨论】:

    • 这些用于将 wchar_t (UTF-16/UTF-32) 转换为 UTF-8。由于字符串已经是 UTF-8,因此不需要转换。
    • @Martin:不能保证字符串是 UTF-8。如果源文件是使用代码页 437 保存的,则字符 à 将是一个值为 133 的单字节。(在 Unicode 中,à 由代码点 U+00E0 表示,UTF-8 将其编码为字节序列 [0xc3, 0xa0]。)
    猜你喜欢
    • 1970-01-01
    • 2013-09-11
    • 1970-01-01
    • 1970-01-01
    • 2013-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多