【问题标题】:How to display accented characters with a C++ program on all platforms?如何在所有平台上使用 C++ 程序显示重音字符?
【发布时间】:2015-03-18 01:48:07
【问题描述】:

我正在尝试将 C++11 程序从 Windows 移植到 Linux (GCC-4.9)。 本来我只是在程序里面设置locale

setlocale(LC_ALL, "");

但是,它在 Linux(Linux Mint 的最新版本)上显示缺少字符。然后我继续将所有源文件保存为 UTF-8 格式,这解决了 linux 下的问题,但现在所有字符在 windows 中都搞砸了。

如果有帮助,语言是法语。 有没有什么方法可以在两个平台下正确显示文字而不用太麻烦?

非常感谢您的帮助,谢谢。

void EcranBienvenue()
{
    char coinHG = (char)201;
    char coinHD = (char)187;
    char coinBG = (char)200;
    char coinBD = (char)188;
    char ligneH = (char)205;
    char ligneV = (char)186;
#ifdef _WIN32
    system("cls");
#elif defined __linux__
        system("clear");
#else
        cout << string(20,'\n');
#endif
    setlocale(LC_ALL, "C");
    cout << coinHG;
    for (int i = 0; i < 48; i++)
        cout << ligneH;
    cout << coinHD << endl;
    cout << ligneV << "                                                " << ligneV << endl;
    cout << ligneV << "     Les productions                 inc        " << ligneV << endl;
    cout << ligneV << "                                                " << ligneV << endl;
    cout << ligneV << "     Système de gestion des abonnements         " << ligneV << endl;
    cout << ligneV << "                                                " << ligneV << endl;
    cout << coinBG;
    for (int i = 0; i < 48; i++)
        cout << ligneH;
    cout << coinBD << endl;
    setlocale(LC_ALL, "");

}

边框在 Linux 上不起作用是正常的。 但是,三行文字会在终端上准确显示。

在 Windows 上,“è”将是一个不正确的字符。

Système de gestion des abonnements 

【问题讨论】:

  • 字符从何而来?你如何展示它们?有一个代码示例会有所帮助。
  • “它在 Linux 上显示丢失的字符” - 用什么方式来表达它......!
  • @TonyD 黑钻石,中间有问号
  • 你必须比这更具体。一件事,在您的源代码中使用 是不可靠的;坚持使用您的源代码中的基本 ASCII (32 - 127)。
  • @Tristan 是的,但你不能通过将它嵌入到源代码中来做到这一点

标签: c++ linux windows utf-8 locale


【解决方案1】:

C++ 没有为(窄)字符串定义任何编码,Windows 使用 CP-1252,而 Linux 使用 UTF-8。使用std::wstringstd::wcout

【讨论】:

  • 并使用L"è" 形式的宽字符文字。尽管您可能需要使用一些特定于编译器的选项才能使其正确读取源代码。
  • 这也不够。通常情况下,如果你写:std::wcout &lt;&lt; L"è"; 会发生什么是写将失败并将wcout 设置为错误状态。在像 Linux 这样的平台上,您可以通过使用适当的语言环境来解决这个问题。在 Windows 上,您也可以尝试这样做,但您将受限于特定的代码页。我强烈建议反对 使用 wchar_t、wcout、wstring 进行可移植代码;这真的不是 wchar 的目的或擅长的:stackoverflow.com/a/11107667/365496
【解决方案2】:

有很多不同的方法可以做这种事情,但肯定有一些不好的方法。我强烈建议避免几件事:

  • 永远不要更改全局 C 或 C++ 语言环境。在大多数情况下,完全避免使用语言环境。
  • 不要使用 wchar_t(除了隐藏在您跨平台实现的 API 中,wchar_t 仅用于您的 Windows 实现)。
  • 除非绝对需要,否则不要使用旧式编码。 (传统编码是除 UTF-8、UTF-32 和 UTF-16 之外的所有编码。

您看到的问题是因为您在使用错误编码的接口之间传递文本数据。

例如:

Système de gestion des abonnements

这是因为您将 UTF-8 编码文本传递到需要使用(可能)Microsoft 代码页 850(您的控制台的 OEM 代码页)编码的数据的接口。

您需要知道接口需要什么编码才能使用它。您还需要知道您的数据使用的是什么编码。为此,您应该选择在代码中使用的一致编码,并在接口边界根据需要将其他数据与该编码相互转换。我相信 UTF-8 是跨平台代码的最佳选择。


由于 MSVC 实现标准 C 和 C++ IO 工具的缺陷,您最好使用本机 Win32 实现来实现自己的 IO API。

Here's 讨论在 Windows 上实现输出功能的页面。

本文实现的打印函数采用 wchar_t 输入。这是将 UTF-8 转换为 UTF-16/wchar_t 的一种方法:

#include <codecvt>
#include <locale>

std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert;

std::string str = "Système de gestion des abonnements";
UPrint(convert.from_bytes().c_str());

此外,您可以实现一个streambuf,它可以正确处理写入Windows 控制台并用它替换std::cout 中的streambuf,这样打印到cout 就会正确打印到控制台。记得在退出前恢复原来的streambuf,这样cout的销毁才能成功。您可以使用 RAII 类型的对象句柄来设置流缓冲区并稍后将其切换回来。

这样的程序可能看起来像:

int main() {
  Set_utf8_safe_streambuf buffer_swapper(std::cout); // on windows swaps cout's streambuf with one that can print UTF-8 to the console, does nothing on other platforms

  std::cout << "Système de gestion des abonnements" << '\n'; // utf-8 data
}

这是一个answer,其中包含一些有关实现和交换流缓冲区的详细信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多