如何在所有平台上使用C++程序显示重音字符

How to display accented characters with a C++ program on all platforms?

本文关键字:程序 C++ 显示 字符 平台      更新时间:2023-10-16

我正在尝试将一个C++11程序从Windows移植到Linux(GCC-4.9)。最初,我只是在程序中设置区域设置

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 

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

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

  • 永远不要更改全局C或C++区域设置。在大多数情况下,完全避开当地
  • 不要使用wchar_t(除了隐藏在跨平台实现的API中之外,只对Windows实现使用wchar_t)
  • 除非绝对需要,否则不要使用遗留编码。(传统编码是除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。

这是一个关于在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
}

以下是关于实现和交换streambuf的一些细节。