在Windows控制台正常打印UTF8字符(Properly print utf8 characte

2019-06-25 04:45发布

这是我尝试做到这一点:

#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