【问题标题】:Characters defined using '\uxxxx' format display the wrong character使用 '\uxxxx' 格式定义的字符显示错误字符
【发布时间】:2013-03-11 15:19:51
【问题描述】:

我正在使用 Dev C++,Windows 7。

我正在尝试使用以下方法打印非 ascii 字符:

char a='\uwxyz';

例如:

#include <locale.h>
#include <iostream>
#include <cstdlib>
#include <windows.h>
#include <conio.h>
#include <stdio.h>

using namespace std;

int main()
{
    setlocale(LC_ALL,"en_US.UTF-8");
    char a='\u0041'; //Should display 'A'
    cout<<a<<endl;
    a='\u2639'; //Should display '☹'
    cout<<a<<endl;
    system("PAUSE");
}

在本例中,大写字母 A 正确显示。使用 wxDev,☹ 字符不会显示任何内容。使用 Dev(我需要将其用于最终程序),我会得到一个扩展的 ascii 字符(它是一个类似于 ∥ 的符号,但有多个符号看起来像这样,我不知道它是哪个)。

在 Dev 和 wxDev 中,☹ 显示为 ?。

在对如何正确显示 unicode 字符进行了一些初步搜索后,我添加了 setlocale,但我还没有找到任何解决此问题的方法。

我无法使用其他编译器或修改系统设置来完成这项工作。 (是的,这是一个学校项目。不,该项目不需要特殊字符;我只是想让它看起来更好。)如果不修改这些设置就无法工作,那也是有用的信息。

提前感谢您的帮助。

编辑:使用 Dev,而不是 wxDev,

char a='\u0041'; //should be A
cout<<a;

我收到一个错误:\u0041 不是有效的通用字符

如果我使用 wchar_t 作为数据类型:

wchar_t a = '\u2639';
cout<<a<<endl;

输出为 39097。

【问题讨论】:

  • char a='\u2639' 不太可能做你想做的事,因为char(根据定义)是一个字节,而\u2639 不能放在一个字节中(至少当一个字节是八位时,几乎总是如此)。对于替代方案,您可以尝试谷歌搜索“宽字符”或wchar

标签: c++ unicode character-encoding


【解决方案1】:

通用字符名称 (UCN) 是一种向编译器传达您要表示的字符的方法。只要您可以将基本源字符提供给编译器,那么每个编译器都会看到相同的 UCN,因此会看到您代表的是相同的字符。

这与在源代码中逐字书写字符相反:

char a = '☹';

由于编译器只需要支持基本的源字符,因此编译器甚至可能无法处理此代码。它实际看到的内容取决于编译器使用的源编码。一个编译器可能会看到您想要的字符,而另一个编译器会看到

char a = 'Â☐¹';

但是,仅仅因为 UCN 能够向编译器指定字符并不意味着:

  • 编译器的执行字符集包含该字符或
  • 数据类型char 可以表示该字符值

在您的情况下,主要问题是 执行字符集 是 Windows 的代码页之一(可能是 CP1252),它没有字符“☹”。所以当编译器将字符'☹'转换为执行字符集时,转换产生'?'而不是你想要的。

我的编译器的执行字符集确实包含字符“☹”,但它恰好有一个多字节表示,所以我的编译器说:

error: character too large for enclosing character literal type
    char a = '☹';
             ^

要真正理解这个主题,您需要了解编码、字符集、它们如何在 C++ 翻译阶段发挥作用,以及它们与编译器对字符和字符串文字的处理有何关系。此外,语言环境实际上与这些无关。语言环境处理运行时行为,而您的问题完全在于编译器对编码的编译时处理。


在一个到处使用 UTF-8 的平台上,以下工作:

#include <iostream>

int main() {
    std::cout << "☹\n";
}

请注意,上面使用的是字符串文字而不是字符文字,以便字符可以扩展为多字节表示。

不幸的是,Windows 不支持这种方式的 Unicode。在 Windows 上更复杂:

#include <Windows.h>
#include <cwchar>

int main() {
    wchar_t const *a = L"\u2639\n";
    DWORD numOfCharsWritten;
    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), a, wcslen(a), &numOfCharsWritten, NULL);
}

不幸的是,即使上面的代码也不太可能显示您想要的内容,因为 Windows 上的控制台通常未配置为能够显示 Unicode 字符“☹”。相反,您可能想查看控制台使用的 OEM 编码(可能是 CP437),查找所需字符的编码,然后打印出该值。例如 CP437 有字符 '☺',你可以这样打印出来:

#include <iostream>

int main() {
    std::cout << "\x01\n"; // ☺ has the value 0x01 in CP 437
}

【讨论】:

  • 如果您使用chcp 65001 运行控制台,并将字体更改为比默认字体更古老的字体,那么std::cout &lt;&lt; "☹\n"; 不应该只在源文件以UTF-8 格式保存并且编译器可以配置成什么都不做吗?它只是直接传递源字节,控制台可以使用代码页 65001 解释它们。同样的想法适用于只有 char* 字符串的 PHP。
  • 这会起作用,尽管阻止编译器在源编码和它的执行编码之间进行转换(通过将文件保存为没有签名的 UTF-8)会破坏宽字符文字。使用 VC++ 没有其他方法可以做到这一点。
  • 感谢您的精彩解释!这也是可以理解的。我会看看我是否可以修复代码页,但如果不能,我至少明白为什么它现在不起作用了。
  • @Esailija 实际上在第二次思考时仍然无法在写出控制台时工作。写入文件时它会正常工作,但 Windows 控制台会尝试将每个单独的字节从 UTF-8 转换。由于各个字节都是部分 UTF-8 序列,因此控制台只会显示每个字节的转换错误。但是,您可以将 cout 的用法替换为 puts 的用法并使其正常工作。
  • @user2197013 不幸的是,VC++ 用于执行字符集的代码页只能通过在编译代码的机器上设置系统“非 Unicode 程序的编码”来配置。然后在每台要运行 exe 的机器上,您可能必须将该机器的“非 Unicode 程序编码”设置为相同的代码页。不是很方便。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-08
  • 1970-01-01
  • 1970-01-01
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多