这是我尝试做到这一点:
#include <stdio.h>
#include <windows.h>
using namespace std;
int main() {
SetConsoleOutputCP(CP_UTF8);
//german chars won't appear
char const* text = "aäbcdefghijklmnoöpqrsßtuüvwxyz";
int len = MultiByteToWideChar(CP_UTF8, 0, text, -1, 0, 0);
wchar_t *unicode_text = new wchar_t[len];
MultiByteToWideChar(CP_UTF8, 0, text, -1, unicode_text, len);
wprintf(L"%s", unicode_text);
}
其效果是,只有美国ASCII字符显示。 没有错误显示。 源文件中UTF8编码。
所以,我在做什么错在这里?
到WouterH:
int main() {
SetConsoleOutputCP(CP_UTF8);
const wchar_t *unicode_text = L"aäbcdefghijklmnoöpqrsßtuüvwxyz";
wprintf(L"%s", unicode_text);
}
- 这也不起作用。 效果是一样的。 我的字体当然是龙力控制台的。
第三个观点:
#include <stdio.h>
#define _WIN32_WINNT 0x05010300
#include <windows.h>
#define _O_U16TEXT 0x20000
#include <fcntl.h>
using namespace std;
int main() {
_setmode(_fileno(stdout), _O_U16TEXT);
const wchar_t *u_text = L"aäbcdefghijklmnoöpqrsßtuüvwxyz";
wprintf(L"%s", u_text);
}
OK,事物开始工作,但输出是: ańbcdefghijklmno÷pqrs▀tuŘvwxyz
。
Answer 1:
另一个技巧,而不是SetConsoleOutputCP
,将使用_setmode在stdout
:
// Includes needed for _setmode()
#include <io.h>
#include <fcntl.h>
int main() {
_setmode(_fileno(stdout), _O_U16TEXT);
wchar_t * unicode_text = L"aäbcdefghijklmnoöpqrsßtuüvwxyz";
wprintf(L"%s", unicode_text);
return 0;
}
不要忘记删除调用SetConsoleOutputCP(CP_UTF8);
Answer 2:
默认情况下,Windows上的宽打印功能不处理ASCII范围之外的字符。
有几种方法可以得到Unicode数据到Windows控制台。
直接使用控制台API,WriteConsoleW。 你必须确保你在实际写入到控制台,当输出到别的使用其他手段。
标准输出文件描述符的模式设置为“的Unicode”模式,_O_U16TEXT或_O_U8TEXT之一。 这将导致宽字符输出功能正常输出Unicode数据到Windows控制台。 如果他们对文件描述符并不代表一个控制台使用,那么它们所造成的字节输出流分别为UTF-16和UTF-8。 NB设置这些模式之后,对相应的流的非宽字符功能是不可用的,并导致崩溃。 你必须只使用宽字符功能。
UTF-8文本可以通过设置控制台输出的代码页CP_UTF8,如果你使用了正确的功能可以直接打印到控制台。 大多数的更高级别的功能,如basic_ostream<char>::operator<<(char*)
不以这种方式工作,但您可以使用较低级别的功能或实现自己的ostream是解决该问题的标准功能的工作原理。
与第三方法的问题是这样的:
putc('\302'); putc('\260'); // doesn't work with CP_UTF8
puts("\302\260"); // correctly writes UTF-8 data to Windows console with CP_UTF8
与大多数的操作系统,在Windows控制台不是简单地接受字节流的另一个文件。 它的创建和程序拥有的,并通过其独特的WIN32 API访问的特殊设备。 的问题是,当控制台被写入,该API看到在于使用其API的传递的数据的准确程度,以及从窄字符宽字符发生而不考虑该数据可能是不完整的转换。 当一个多字节字符使用多于一个呼叫到控制台API通过,各自分别通过片被看作是非法编码,并且将被视为这样。
它应该是很容易的,以解决这一问题,但CRT团队在微软视其为不是他们的问题,而无论球队在控制台上的工作可能并不关心。
你可以通过实现自己的流缓冲子类,手柄做转化为正确的wchar_t解决它。 即占事实的多字节字符的字节可能会分开,保持写操作之间的转换状态(例如, std::mbstate_t
)。
Answer 3:
//Save As UTF8 without signature
#include<stdio.h>
#include<windows.h>
int main() {
SetConsoleOutputCP(65001);
const char unicode_text[]="aäbcdefghijklmnoöpqrsßtuüvwxyz";
printf("%s\n", unicode_text);
}
结果:
aäbcdefghijklmnoöpqrsßtuüvwxyz
Answer 4:
控制台可以设置为显示UTF-8字符:@vladasimovic答案SetConsoleOutputCP(CP_UTF8)
可用于这一点。 另外,您也可以通过DOS命令准备控制台chcp 65001
或通过系统调用system("chcp 65001 > nul")
在主程序中。 不要忘记保存的源代码在UTF-8也是如此。
要检查UTF-8支持,运行
#include <stdio.h>
#include <windows.h>
BOOL CALLBACK showCPs(LPTSTR cp) {
puts(cp);
return true;
}
int main() {
EnumSystemCodePages(showCPs,CP_SUPPORTED);
}
65001
应该出现在列表中。
Windows控制台使用OEM代码页在默认情况下,最默认点阵字体仅支持民族特色。 Windows XP和更新的同时还支持TrueType字体,这应该显示丢失字符(@Devenec表明龙力控制台在他的回答)。
为什么失败的printf
正如@在他的回答bames53点,Windows控制台是不是流的设备,你需要写多字节字符的所有字节。 有时printf
食堂工作,由一个把字节输出缓冲区之一。 尝试使用sprintf
,然后puts
结果,或强制fflush只累计输出缓冲区。
如果一切失败
注意UTF-8格式 :一个字符显示为1-5字节。 使用此功能将转移到字符串中的下一个字符:
const char* ucshift(const char* str, int len=1) {
for(int i=0; i<len; ++i) {
if(*str==0) return str;
if(*str<0) {
unsigned char c = *str;
while((c<<=1)&128) ++str;
}
++str;
}
return str;
}
......而这个函数来转换成字节Unicode数字:
int ucchar(const char* str) {
if(!(*str&128)) return *str;
unsigned char c = *str, bytes = 0;
while((c<<=1)&128) ++bytes;
int result = 0;
for(int i=bytes; i>0; --i) result|= (*(str+i)&127)<<(6*(bytes-i));
int mask = 1;
for(int i=bytes; i<6; ++i) mask<<= 1, mask|= 1;
result|= (*str&mask)<<(6*bytes);
return result;
}
然后,你可以尝试使用一些野生/古/非标WINAPI样MultiByteToWideChar函数(不要忘记调用setlocale()
前!)
或者您可以使用从Unicode表自己的映射到活动的工作代码页。 例:
int main() {
system("chcp 65001 > nul");
char str[] = "příšerně"; // file saved in UTF-8
for(const char* p=str; *p!=0; p=ucshift(p)) {
int c = ucchar(p);
if(c<128) printf("%c\n",c);
else printf("%d\n",c);
}
}
这应该打印
p
345
237
353
e
r
n
283
如果你的代码页不支持捷克标点符号,你可以映射345 => R,237 => I,353 => S,283 =>即 至少有5(!)不同的字符集只是捷克。 要在不同的Windows区域显示可读字符是一个恐怖。
Answer 5:
我有类似的问题,但没有一个现有的答案为我工作。 别的东西,我观察到的是,如果我在一个普通的字符串坚持UTF-8字符的文字,他们会正确打印,但如果我试图用UTF-8文字( u8"text"
),该字符获取由编译器屠杀(通过打印他们的数字值每次一个字节证明; 原始文字具有正确的UTF-8字节,作为Linux机器上验证,但UTF-8字面是垃圾)。
一些闲逛之后,我找到了解决办法: /utf-8
就这样,什么都行; 我的消息来源是UTF-8,我可以使用显式UTF-8文字,输出的作品具有不需要其他的变化。
Answer 6:
我解决了以下方法问题:
龙力控制台似乎不支持变音符号,因此改变控制台字体索拉,例如,工作。
#include <stdio.h>
#include <Windows.h>
int main()
{
SetConsoleOutputCP(CP_UTF8);
// I'm using Visual Studio, so encoding the source file in UTF-8 won't work
const char* message = "a" "\xC3\xA4" "bcdefghijklmno" "\xC3\xB6" "pqrs" "\xC3\x9F" "tu" "\xC3\xBC" "vwxyz";
// Note the capital S in the first argument, when used with wprintf it
// specifies a single-byte or multi-byte character string (at least on
// Visual C, not sure about the C library MinGW is using)
wprintf(L"%S", message);
}
编辑:固定笨错别字和串的译码文字,关于那些遗憾。
Answer 7:
UTF-8没有for Windows控制台工作。 期。 我曾尝试没有成功的所有组合。 问题的出现是由于不同ANSI / OEM字符的分配等等一些答案说是没有问题的,但这样的答案可能来自使用7位ASCII纯程序员或具有相同的ANSI / OEM代码页(中国,日本)。
无论你坚持使用UTF-16和宽字符函数(但你仍然仅限于您的OEM代码页的256个字符 -除了中国/日本),或在您的源文件使用OEM代码页的ASCII字符串。
是的,这是一个混乱的。
对于多语言的程序我使用字符串资源,并写了LoadStringOem()
函数,自动转换使用UTF-16资源OEM字符串WideCharToMultiByte()
没有中间缓冲。 随着Windows的自动选择正确的语言出来的资源,它有望加载即转换为目标OEM代码页语言的字符串。
因此,你不应该使用美国英语语言资源的8位字符印刷(如省略号......和引号“”)作为英语(美国)是由Windows时选择没有语言匹配已检测(即后备)。 举个例子,你有而不是在德国,捷克,俄罗斯,英国和美国,以及用户已在中国,他/她会看到英语资源,再加上你的垃圾制作精美的排版,如果你让你的文字好看的。
现在,Windows 7和10, SetConsoleOutputCP(65001/*aka CP_UTF8*/)
按预期工作。 你应该保持编译源文件使用UTF-8无BOM,否则,你的字符串字面量将被重新编码为ANSI。 此外,控制台字体必须包含所希望的字符,且不能是“终端”。 不幸,没有字体涵盖变音符号和字符中国,甚至当你同时安装语言包,所以一次不能真正显示所有字符的形状。
文章来源: Properly print utf8 characters in windows console