【问题标题】:Unicode literals in Visual C++Visual C++ 中的 Unicode 文字
【发布时间】:2014-09-24 04:26:51
【问题描述】:

考虑以下代码:

#include <string>
#include <fstream>
#include <iomanip>

int main() {
    std::string s = "\xe2\x82\xac\u20ac";
    std::ofstream out("test.txt");
    out << s.length() << ":" << s << std::endl;
    out << std::endl;
    out.close();
}

在 Linux (Ubuntu 14.04) 上的 GCC 4.8 下,文件 test.txt 包含以下内容:

6:€€

在 Windows 上的 Visual C++ 2013 下,它包含以下内容:

4:€\x80

(“\x80”是指单个 8 位字符 0x80)。

我完全无法让任一编译器使用std::wstring 输出 字符。

两个问题:

  • Microsoft 编译器究竟认为它对char* 文字做了什么?它显然在做一些事情来编码它,但不清楚。
  • 使用std::wstringstd::wofstream 重写上述代码以输出两个 字符的正确方法是什么?

【问题讨论】:

  • L"\x20ac\x20ac" Windows 上 8 位字符串的编码是环境 8 位代码页,在美国是 1252。您正在使用 utf8。 (您还将输出文件解释为 utf8 而不是 1252。)
  • 公平一点 - Windows 上的“它包含这个”是根据 Notepad++ 的编码设置为 UTF-8。
  • 嗯,systeminfo 将系统和输入本地人都指定为“en-gb;English (United Kingdom)”,想一想这是否是 UTF-8 语言环境,它没有说明。
  • 没有 UTF-8 语言环境这样的东西。代码页 65001 (UTF-8) 不能是活动代码页。
  • 那么“en_GB.utf8”是什么?

标签: visual-c++ unicode unicode-escapes unicode-literals


【解决方案1】:

这是因为您使用的是\u20ac,它是 ASCII 字符串中的 Unicode 字符文字。

MSVC 将"\xe2\x82\xac\u20ac" 编码为0xe2, 0x82, 0xac, 0x80,,即4 个窄字符。它本质上将\u20ac 编码为0x80,因为它将欧元字符映射到标准1252 codepage

GCC 正在将 Unicode 文字 /u20ac 转换为 3 字节 UTF-8 序列 0xe2, 0x82, 0xac,因此生成的字符串最终为 0xe2, 0x82, 0xac, 0xe2, 0x82, 0xac

如果您使用 std::wstring = L"\xe2\x82\xac\u20ac",它会被 MSVC 编码为 0xe2, 0x00, 0x82, 0x00, 0xac, 0x00, 0xac, 0x20,这是 4 个宽字符,但由于您将手工创建的 UTF-8 与 UTF-16 混合,因此生成的字符串不会产生太大影响感觉。如果你使用std::wstring = L"\u20ac\u20ac",你会在一个宽字符串中得到 2 个 Unicode 字符,正如你所期望的那样。

下一个问题是 MSVC 的 ofstream 和 wofstream 总是以 ANSI/ASCII 写入。要让它以 UTF-8 编写,您应该使用 &lt;codecvt&gt;(VS 2010 或更高版本):

#include <string>
#include <fstream>
#include <iomanip>
#include <codecvt>

int main()
{
    std::wstring s = L"\u20ac\u20ac";

    std::wofstream out("test.txt");
    std::locale loc(std::locale::classic(), new std::codecvt_utf8<wchar_t>);
    out.imbue(loc);

    out << s.length() << L":" << s << std::endl;
    out << std::endl;
    out.close();
}

并编写 UTF-16(或更具体地说是 UTF-16LE):

#include <string>
#include <fstream>
#include <iomanip>
#include <codecvt>

int main()
{
    std::wstring s = L"\u20ac\u20ac";

    std::wofstream out("test.txt", std::ios::binary );
    std::locale loc(std::locale::classic(), new std::codecvt_utf16<wchar_t, 0x10ffff, std::little_endian>);
    out.imbue(loc);

    out << s.length() << L":" << s << L"\r\n";
    out << L"\r\n";
    out.close();
}

注意:对于 UTF-16,您必须使用二进制模式而不是文本模式以避免损坏,因此我们不能使用 std::endl 而必须使用 L"\r\n" 来获取正确的行尾文本文件行为。

【讨论】:

  • 感谢您的回答。我是否认为 GCC 不支持 std::codecvt_utf8
  • 次要更正:“它将 \u20ac 编码为 0x80,因为 Unicode 字符 U+20AC 在代码页 1252 (see table) 中的位置 80。”
  • @Raymond - 非常棒。感谢您的澄清!我会解决的。
猜你喜欢
  • 2013-11-02
  • 1970-01-01
  • 1970-01-01
  • 2017-07-10
  • 2011-05-15
  • 2015-03-14
  • 1970-01-01
  • 2012-05-20
  • 1970-01-01
相关资源
最近更新 更多