【问题标题】:Swedish characters don't compare correctly瑞典语字符无法正确比较
【发布时间】:2014-04-06 11:13:04
【问题描述】:

由于某种原因,If/else 语句在 C++ 中对我来说无法正常工作

问题是当变量等于右边(höger)时,它不会输出 If 语句,而是会继续执行 else 语句。如果我将字母 'ö' 替换为 say 'o' 所以它变成了 'hoger',那么 if 语句将起作用。所以每当我写“höger”这个词时,它不会进入 if 语句,而是会进入 else 语句。但是,如果我使变量等于“hoger”,然后我写“hoger”,它将起作用。如果 If 语句识别它,我怎样才能写出 'höger' 呢?好像瑞典字母不起作用。

我的代码如下所示:

#include <iostream>
#include <string>

using namespace std;


int main() {
    setlocale(LC_ALL,"");


    string test; // Define variabel
    cout << " Höger elle vänster"<<endl; // Right or left
    cin >> test;


    if(test == "höger") { // If right, then output this.

        cout <<"Du valde höger"<<endl;

    } 

    else if(test == "vänster") { // If left, then output this

        cout <<"Du valde vänster"<<endl;

    } else {

        // Do this

    }


}

【问题讨论】:

  • 源文件的编码是什么?你的终端是什么编码的?
  • 记住比较运算符是区分大小写的。
  • 你得到什么尝试 std::cout
  • 哦,你的逻辑有缺陷。如果输入不是"höger",则不会自动表示它是"vänster"。如果用户输入其他内容怎么办?
  • 这只是最有可能测试奇怪“o”的测试......

标签: c++ if-statement statements setlocale


【解决方案1】:

问题几乎肯定与编码有关。

C/C++ 语言规范不会自动处理除 7 位 ASCII 以外的任何内容。 o-umlaut 字符超出该范围,具体行为取决于源代码文件的编码。

最可能的可能是 ISO 8859-1、Windows ANSI-1252、UTF-8 或 Windows OEM 850。前两个对这个字符进行相同的编码,但在其他每个中它是不同的。

了解更多有关您正在使用的编码和工具集的信息,可能会提供更具体的诊断和建议。

[顺便说一句,C/C++ 中的 if/else 语句工作得很好,谢谢。]


如果我们暂时假设这是 Windows 和 Visual C++,那么这就是您要处理的内容。

  • 在 Visual Studio 中编写的源代码:代码页 1252。o-umlaut 字符的代码点是 0xf6。
  • 从控制台读取的键盘输入:代码页 850。o-umlaut 字符的代码点是 0x94。

显然不是很好的匹配。但是,Visual Studio 也可以非常愉快地编辑多种编码的源代码文件,包括 UTF-8(带字节标记)、UTF-16(宽字符)和代码页 850。所以:

  • 在 Visual Studio 中编写的源代码:代码页 850。o-umlaut 字符的代码点是 0x94。现在可以了。

您还可以使用 CHCP 命令更改控制台的代码页。

  • 将控制台更改为 CHCP 1252 即可。

标准要求编译器在读取源代码时的行为必须与执行字符集保持一致。见 n3797 S2.2.5:

字符文字或字符串文字中的每个源字符集成员,以及每个转义 字符文字或非原始字符串文字中的序列和通用字符名称,转换为执行字符集的相应成员

S2.3/3:

基本执行字符集和基本执行宽字符集应分别包含基本源字符集的所有成员,加上表示警报、退格和回车的控制字符,加上一个空字符(分别为空宽字符),其表示全为零。对于每个基本执行字符集,成员的值应该是非负的并且彼此不同。在源和执行基本字符集中,上述十进制数字列表中 0 之后的每个字符的值都应比前一个字符的值大 1。执行字符集和执行宽字符集是基本执行字符的实现定义的超集 set 和基本执行宽字符集。执行字符集成员和附加成员集的值是特定于语言环境的。

n3797 S2.14.3/1:

不以 u、U 或 L 开头的字符文字是普通字符文字,也称为窄字符文字。包含单个可在执行字符集中表示的 c-char 的普通字符文字具有 char 类型,其值等于执行字符集中 c-char 编码的数值。

n3297 S2.14.5/6:

不以编码前缀开头的字符串字面量是普通字符串 字面量,并使用给定的字符进行初始化。

执行字符集是实现定义的。微软关于 C 编译器实现定义行为的声明在这里:http://msdn.microsoft.com/en-us/library/hx3yt8af.aspx。 [我找不到单独的 C++ 版本,所以我认为这两者都适用。]

The source character set is the set of legal characters that can appear in source files. For Microsoft C, the source character set is the standard ASCII character set.

对不起,语言律师的东西,但这说明 MSVC 编译器独立于语言环境/编码并实现 8 位 ASCII,未指定代码页。显然,标准库函数可能需要了解各种用途的编码,但那是另一回事了。


最后一点,Microsoft C 编译器可以追溯到大约 30 年前,比 Windows 早。在代码页 850 中编写源代码并使其在控制台上正确运行始终是可能的,但需要仔细处理扩展(8 位)字符。许多人仍然这样做。这里的问题是用 Windows-Ansi 或 Unicode 编写的源代码和来自 OEM (cp850) 控制台的键盘输入。更改其中任何一个以使其正常工作。

【讨论】:

  • 严重误导:“确切的行为取决于源代码文件的编码”。只有不正确的编译器调用才会出现这种情况。对于 Visual C++,唯一实用的方法是使用 UTF-8 编码无 BOM,这在 Windows 中是非常不寻常的(许多程序,不仅仅是 VC 编译器,会误解这样的文件)。但是,使用 g++ 默认 是不检查窄编码,默认 g++ 窄执行字符集是 UTF_8,例如由于源文件编码,Windows 中的 MinGW g++ 确实会出现问题。 g++ 修复:指定编码。 ;-)
  • -1 re“在 Visual Studio 中编写的源代码:代码页 850。o-umlaut 字符的代码点是 0x94。现在它可以工作了。”是非常不好的建议。这意味着例如排序和字符分类将产生不正确的结果,因为编译器假定源代码被编码为 Windows ANSI。 Visual C++ 从文件内容中确定源编码,并识别带有 BOM 的 UTF-8 以及 UCS2/UTF-16——但它无法识别它在源编码方面被骗了。
  • @Cheersandhth.-Alf:错了。编译器工作得很好,没有做这样的假设。库函数进行排序和分类,它们需要知道正确的语言环境/编码才能正常工作。
  • 尝试在main 的开头添加setlocale( LC_ALL, "" )。这使得代码页 850 控制台中的输出不正确。别介意排序和分类也不正确,只要注意输出本身也是错误的——当然,你也是。错误的。错误的。而且……错了。完全和完全错误。错了。
【解决方案2】:

实际上这个问题只会在 Windows 中表现出来,所以我假设是 Windows。

那么问题是C++窄扩展执行字符集(1)(编码)与控制台窗口使用的编码不匹配。 “窄”是指char 类型。 “执行字符集”是 C++ 标准使用的正式术语,指的是假定用于存储在可执行文件中的文本的编码。编译器将源代码文字转换为此编码。它也被假定用于与任何外部编码的转换,例如与控制台编码的转换。

      

对于 Visual C++,无论源代码编码如何,窄编码始终是 Windows ANSI(2),除非你欺骗了编译器。假设您使用的是 Visual C++,那么这就是您知道的一种编码。

默认情况下,控制台窗口中的编码是用于原始 IBM PC 的编码,在您的情况下可能是 代码页 850(原始 IBM PC 英语代码页 437 的西欧变体)。运行 Windows 命令解释器 cmdWindows-key+R,键入 cmd,确定)。键入 chcp 以检查当前代码页。键入chcp 1252 以切换到Windows ANSI Western,这可能是您机器上的Windows ANSI 代码页。运行您的程序 [.exe] 文件,例如通过输入其完整路径,或转到其目录并仅输入其名称,例如

[H:\开发\测试\0046] > cl /nologo /EHsc /GR encoding.cpp /Fe:b.exe 编码.cpp [H:\开发\测试\0046] > chcp & b 活动代码页:850 Höger elle vänster 霍格 这里没有输出,不相等。 [H:\开发\测试\0046] > chcp 1252 活动代码页:1252 [H:\开发\测试\0046] > b Höger elle vänster 霍格 杜瓦尔德霍格 [H:\开发\测试\0046] > _

...cl(原始“Lattice C”的缩写)是 Visual C++ 编译器。

您可以通过运行regedit 转到此注册表项来更永久地更改控制台代码页:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage

并在右窗格的列表中双击名为 OEMCP(原始设备制造商代码页的缩写,指 IBM PC)的值,将其更改为 1252 或更多一般设为与ACP 相同的值,然后重启机器。

哦,还需要将控制台窗口字体更改为 TrueType 字体,例如 Lucida Console,因为默认是(模拟的)位图字体,只能在原始控制台上正常工作代码页。您可以右键单击控制台窗口标题以获取菜单,选择[默认],并配置默认字体、大小、颜色等。更改不会影响当前控制台窗口,但它们将应用于以后的控制台窗口,除了对于已经单独配置的那些(3)

这种控制台窗口配置的替代方法是使用 Console2 程序。如果这样做,那么在 Windows 7 及更高版本中请务必使用 64 位版本。否则某些操作(例如调用 64 位程序的链接)将不起作用。


总结一下,你可以

  • 从命令解释器运行程序(使用chcp更改代码页),或

  • 更永久地更改控制台代码页,如上所述。

在任何一种情况下,将控制台窗口字体更改为 TrueType 字体都是一个好主意 - 是的,这会影响功能,而不仅仅是外观。

注意 Microsoft 的其他荒谬之处:在 Windows 7 及更高版本中,控制台窗口中默认使用的“系统”字体实际上,在幕后是一种带有成千上万字形的 TrueType 字体,但它被使用了模拟旧的 16 位 Windows 位图字体,具有同样愚蠢的限制,因此您仍然需要更改为其他一些 TrueType 字体...


(1)参见 C++11 标准 §2.3/3。

(2) “Windows ANSI”取决于 Windows 配置,并且始终是由GetACP API 函数指定的代码页。实际上,此函数从上面引用的注册表键/值中获取其值。然而,这在很大程度上是没有记录的

(3) 在 Windows XP 中,Windows 会询问您是否要保存单个控制台窗口配置。从 Windows Vista 开始,它会毫无疑问地保存,也没有任何信息表明它已保存。没有用于删除此类已保存配置的用户界面,但可以通过以编程方式更改快捷方式文件和/或通过注册表编辑来删除它们,但这是一种不切实际且脆弱的解决方案。

【讨论】:

  • 抱歉所有的编辑,但这是一个复杂得离谱的问题。我希望微软能够齐心协力。唉,显然是非技术人员负责。
  • With Visual C++ the narrow encoding is always Windows ANSI(2), regardless of source code encoding -- 不是这样。 VC++ 愉快地编译了代码页 850,以及 UTF-8 和 UTF-16。什么进去,什么出来。
  • @david.pfx:重新“不是这样”和“什么进去,出来”。你错了。你为什么发布这样的虚假声明,甚至都懒得去检查。我将您的评论标记为“不具建设性”。因为传播虚假信息绝对没有建设性。
  • 你说的,那就证明吧。它在哪里说 MSVC++ 仅适用于 Windows-Ansi?什么测试会证明它是这样的?
  • 这是一个可爱的小图表,但是您是否测试了用代码页 850 编写的输入?我做到了。
【解决方案3】:

我对您的代码所做的唯一更改如下:

// setlocale(LC_ALL, "");
char *l = setlocale(LC_ALL, NULL);
cout << "Current Locale: " << l << endl;

因为我没有“ISO”keyboard layout,所以我使用了Alt code 来输入我需要的字符。以下是我用于不同代码页的组合键。

以下是我在执行之间更改代码页时的输出

【讨论】:

  • 请注意,第一个示例(代码页 437 使用 Alt 246)相当于在该代码页处于活动状态时在斯堪的纳维亚键盘上键入“ö”。在使用代码页 850(模拟)系统字体的斯堪的纳维亚 PC 上,键入“ö”会显示为“ö”,而这些代码页中的字符 246 是“÷”,如屏幕截图所示。另请注意,setlocale 通常对于正确(窄文本)字母顺序和字符分类是必需的。
【解决方案4】:

问题似乎在于您的 IDE 编译源文件时的编码。如果您使用的是 Visual Studio,您可以像这样更改编码设置:

【讨论】:

  • 请勿截图回复;而是提供更准确的信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-04
  • 2013-03-12
  • 2012-11-30
  • 2015-01-17
  • 1970-01-01
  • 1970-01-01
  • 2012-08-13
相关资源
最近更新 更多