C++ Visual Studio字符编码问题

j8yoct9x  于 2023-08-07  发布在  其他
关注(0)|答案(9)|浏览(117)

不能把我的头绕在这一个是一个真实的的耻辱来源...
我正在使用法语版本的Visual Studio(2008),在法语Windows(XP)中。发送到输出窗口的字符串中的法语口音被损坏。同样从输出窗口输入。典型的字符编码问题,我输入ANSI,得到UTF-8作为返回,或者类似的东西。什么设置可以确保在输出窗口显示“硬编码”字符串时字符保持ANSI格式?
编辑:
范例:

#include <iostream>

int main()
{
std:: cout << "àéêù" << std:: endl;

return 0;
}

字符串
将在输出中显示:
欧菲
(here编码为HTML,以满足您的观看乐趣)
我真的希望它能显示:
àéêù

yhived7q

yhived7q1#

在我继续之前,我应该提到你正在做的不是c/c兼容的。specification在2.2中说明了在源代码中哪些字符集是有效的。里面没什么,所有的字符都是ASCII码的。所以...下面的一切都是关于一个特定的实现(碰巧,VC 2008在美国本地化机器上)。
首先,cout行上有4个字符,输出上有4个字形。所以问题不是UTF8编码的问题,因为它会将多个源字符组合为更少的字形。
从源字符串到控制台上的显示,所有这些都起着作用:
1.您的源文件使用的编码(即C
文件将如何被编译器看到)
1.你的编译器对字符串字面量做了什么,以及它理解什么源编码

  1. <<如何解释传入的编码字符串
    1.控制台需要的编码
    1.控制台如何将输出转换为字体字形。
    现在...
    1和2是比较简单的。看起来编译器猜测源文件的格式,并将其解码为内部表示。无论源编码是什么,它都会生成当前代码页中对应数据块的字符串文字。我没有找到明确的细节/控制。
    3更容易。除了控制代码,<<只向下传递char * 的数据。
    4由SetConsoleOutputCP控制。它应该默认为您的默认系统代码页。您还可以使用GetConsoleOutputCP来确定您拥有的是哪一个(通过SetConsoleCP,输入控制方式不同)
    五是有趣。我敲了敲脑袋,想弄清楚为什么我用CP 1252(西欧,windows)无法让é正确显示。结果是,我的系统字体没有该字符的字形,并有用地使用了我的标准代码页的字形(大写Theta,如果我没有调用SetConsoleOutputCP,我会得到相同的字形)。为了修复它,我不得不将我在控制台上使用的字体改为Lucida Console(一种真正的类型字体)。
    我从这个中学到了一些有趣的东西:
  • 源代码的编码并不重要,只要编译器可以解决它(值得注意的是,将其更改为UTF8并不会更改生成的代码。我的“é”字符串仍然用CP 1252编码为233 0
  • VC正在为我似乎无法控制的字符串文字选择代码页。
  • 控制控制台显示的内容比我预期的要痛苦得多

这对你来说意味着什么以下是一些建议:

  • 不要在字符串文字中使用非ASCII。使用资源,其中 * 您 * 控制编码。
  • 确保你知道你的控制台所期望的编码,并且你的字体有字形来表示你发送的字符。
  • 如果你想弄清楚在你的例子中使用了什么编码,我建议把字符的实际值打印成一个整数。char * a = "é"; std::cout << (unsigned int) (unsigned char) a[0]对我来说确实显示了233,这恰好是CP 1252中的编码。

顺便说一句,如果你得到的是“ÓÚ 3 ¨”而不是你粘贴的内容,那么看起来你的4个字节在某个地方被解释为CP850

chy5wohz

chy5wohz2#

因为我被要求,我会做一些巫术。其他答案来自2009年,但这篇文章仍然出现在我2018年的搜索中。今天的情况大不相同。此外,即使在2009年,公认的答案也是不完整的。

源字符集

每个编译器(包括Microsoft的Visual Studio 2008和更高版本,gcc,clang和icc)都可以读取以BOM开头的UTF-8源文件,并且clang不会读取除UTF-8之外的任何内容,因此带有BOM的UTF-8是C和C源文件的最低公分母。
语言标准没有说明编译器需要支持什么源字符集。一些真实世界的源文件甚至以与ASCII不兼容的字符集保存。2008年的Microsoft Visual C
支持带有字节顺序标记的UTF-8源文件,以及两种形式的UTF-16。如果没有字节顺序标记,它将假定文件是用当前8位代码页编码的,该代码页始终是ASCII的超集。

执行字符集

2012年,编译器在CL.EXE中添加了一个/utf-8开关。今天,它还支持/source-charset/execution-charset开关,以及/validate-charset来检测您的文件是否实际上不是UTF-8。This page on MSDN has a link to the documentation on Unicode support for every version of Visual C++.
当前版本的C标准规定编译器必须同时具有执行字符集和执行宽字符集,执行字符集确定字符常量(如'a')的数值,执行宽字符集确定宽字符常量(如L'é')的值。
对于语言律师来说,标准中很少有关于这些必须如何编码的要求,但Visual C和C
设法打破了它们。它必须包含大约100个不能有负值的字符,并且数字'0''9'的编码必须连续。大写字母和小写字母都不需要,因为它们不在一些旧的大型机上。(也就是说,'0'+9必须与'9'相同,但今天仍有一个实际使用的编译器,其默认行为是'a'+9不是'j',而是'«',这是法律的的。)宽字符执行集必须包括基本执行集,并有足够的位来容纳任何支持的语言环境的所有字符。每个主流编译器都支持至少一个Unicode语言环境,并理解用\Uxxxxxxxx指定的有效Unicode字符,但不支持的编译器可以声称遵守该标准。
Visual C和C++违反语言标准的方式是使其wchar_t UTF-16,它只能表示一些字符作为代理对,而标准规定wchar_t必须是固定宽度编码。这是因为微软早在20世纪90年代就将wchar_t定义为16位宽,当时Unicode委员会还没有发现16位对整个世界来说是不够的,微软也没有打算破坏Windows API。它也支持标准的char32_t类型。

UTF-8字符串

这个问题引发的第三个问题是如何让编译器在内存中将字符串文字编码为UTF-8。从C++11开始,你就可以写这样的东西了:

constexpr unsigned char hola_utf8[] = u8"¡Hola, mundo!";

字符串
不管源字符集是UTF-8、UTF-16、Latin-1、CP 1252还是IBM EBCDIC 1047(这是一个愚蠢的理论示例,但为了向后兼容,它仍然是IBM Z系列大型机编译器的默认值),这都将把字符串编码为以空结尾的UTF-8字节表示。也就是说,它等效于用{ 0xC2, 0xA1, 'H', /* ... , */ '!', 0 }初始化数组。
如果输入字符太不方便,或者如果你想区分表面上相同的字符,如空格和非中断空格,或者预组合和组合字符,你也可以使用通用字符转义符:

constexpr unsigned char hola_utf8[] = u8"\u00a1Hola, mundo!";


无论源字符集如何,也无论文字存储为UTF-8、UTF-16还是UCS-4,都可以使用这些字符集。它们最初是在C99中添加的,但Microsoft在Visual Studio 2015中支持它们。

**编辑:**正如Matthew所报告的,u8"字符串在某些版本的MSVC中存在错误,包括19.14。事实证明,文字非ASCII字符也是如此,即使您指定/utf-8/source-charset:utf-8 /execution-charset:utf-8。上面的示例代码在19.22.27905中可以正常工作。

但是,还有另一种方法可以在Visual C或C++ 2008中工作:八进制和十六进制转义码。在该版本的编译器中,您将使用以下代码对UTF-8文字进行编码:

const unsigned char hola_utf8[] = "\xC2\xA1Hello, world!";

3duebb1j

3duebb1j3#

试试这个:

#include <iostream>
#include <locale>

int main()
{
 std::locale::global(std::locale(""));
 std::cout << "àéêù" << std::endl;

 return 0;
}

字符串

lfapxunr

lfapxunr4#

使用_setmode()工作于¹,并且可以说比更改代码页或设置区域设置更好,因为它实际上会使您的程序输出为Unicode,因此将是一致的-无论当前设置的代码页或区域设置如何。
范例:

#include <iostream>
#include <io.h>
#include <fcntl.h>

int wmain()
{
    _setmode( _fileno(stdout), _O_U16TEXT );
    
    std::wcout << L"àéêù" << std::endl;

    return 0;
}

字符串
在Visual Studio中,确保您将项目设置为Unicode(右键单击 * 项目 * ->单击 * 常规 * -> * 字符集 * = * 使用Unicode字符集 )。
MinGW用户:
1.同时定义UNICODE_UNICODE
1.将-finput-charset=iso-8859-1添加到 compiler options 以解决此错误:“
转换为执行字符集:无效参数 *”
1.将-municode添加到 * 链接器选项 * 中,以绕过“undefined reference to `WinMain@16”(read more)。

**编辑:**设置unicode input 的等价调用为:_setmode( _fileno(stdin), _O_U16TEXT );
**编辑2:**一个重要的信息,特别考虑到问题使用std::cout。不支持此操作。MSDN Docs声明(强调我的):

Unicode模式适用于宽打印函数(例如wprintf),而不支持窄打印函数。在Unicode模式流上使用窄打印函数会触发Assert。
因此,当控制台输出模式为_O_U16TEXT时,不要使用std::cout;同样,当控制台输入为_O_U16TEXT时,不要使用std::cin。您必须使用这些工具的宽版本(std::wcoutstd::wcin)。
请注意,不允许在同一输出中使用mixing cout and wcout(但我发现,如果在窄操作和宽操作之间切换之前先调用flush(),然后再调用_setmode(),它就可以工作)。

a2mppw5e

a2mppw5e5#

我尝试了以下代码:

#include <iostream>
#include <fstream>
#include <sstream>

int main()
{
    std::wstringstream wss;
    wss << L"àéêù";
    std::wstring s = wss.str();
    const wchar_t* p = s.c_str();
    std::wcout << ws.str() << std::endl;

    std::wofstream file("C:\\a.txt");
    file << p << endl;

    return 0;
}

字符串
调试器显示wss、s和p都具有预期值(即“àéêù”),输出文件也是如此。然而,控制台上出现的却是‘’。
因此,问题出在Visual Studio控制台,而不是C++。我引用了巴巴尔的精彩回答,补充道:

SetConsoleOutputCP(1252);


作为第一行,然后控制台输出显示为它应该显示的样子。

uxh89sit

uxh89sit6#

//Save As Windows 1252
#include<iostream>
#include<windows.h>

int main()
{
    SetConsoleOutputCP(1252);
    std:: cout << "àéêù" << std:: endl;
}

字符串
Visual Studio不支持C++的UTF 8,但部分支持C:

//Save As UTF8 without signature
#include<stdio.h>
#include<windows.h>

int main()
{
    SetConsoleOutputCP(65001);
    printf("àéêù\n");
}

pvabu6sv

pvabu6sv7#

请确保不要忘记将控制台的字体更改为Lucida Consolas,如Bahbar所述:这在我案例中是至关重要的(French win7 64 bit with VC 2012)。
然后,如其他人所提到的,使用C++的SetConsoleOutputCP(1252),但它可能会失败,具体取决于可用的页面,因此您可能需要使用GetConsoleOutputCP()来检查它是否工作,或者至少检查SetConsoleOutputCP(1252)是否返回零。更改全局区域设置也可以(由于某些原因,不需要执行cout.imbue(locale());但它可能会打破一些图书馆!

C中,SetConsoleOutputCP(65001);或者基于区域设置的方法对我有用一旦我将源代码保存为UTF8而没有签名(向下滚动,sans-signature选项在页面列表的下方)。
输入使用SetConsoleCP(65001);失败对我来说显然是由于一个坏的实施页65001在windows中。locale方法在C和C++中都失败了。似乎需要一个更复杂的解决方案,不依赖于本机字符,而是依赖于wchar_t。

omhiaaxx

omhiaaxx8#

我在中文输入方面也遇到了同样的问题。我的源代码是utf8,我在编译器选项中添加了/utf-8。它在c++ wide-string和wide-char下工作正常,但在narrow-string/char下不工作,它在Visual Studio 2019调试器和我的SQL数据库中显示乱码。因为要转换成SQLAPI++的SAString,所以我必须使用窄字符。最终,我发现检查以下选项(控制面板->区域->管理->更改系统区域设置)可以解决这个问题。我知道这不是一个理想的解决方案,但它确实帮助了我。
x1c 0d1x的数据

92vpleto

92vpleto9#

在visual studio File中->保存yourSource.cpp As

然后它会弹出一个对话框,问你是否要替换现有的文件,你选择是。
然后弹出这个对话框:

选择带有签名的UTF-8。这解决了我在控制台和文件上的giberish输出问题。
这也符合@Davislor的回答:
带有BOM的UTF-8是C和C++源文件的最小公分母

相关问题