Redirect wprintf输出能否在Windows上正确重定向到UTF-16?

Redirect wprintf输出能否在Windows上正确重定向到UTF-16?,redirect,encoding,utf-16,Redirect,Encoding,Utf 16,在C程序中,我使用wprintf在Windows控制台中打印Unicode(UTF-16)文本。这可以正常工作,但是当程序的输出被重定向到日志文件时,日志文件的UTF-16编码已损坏。 在Windows命令提示符下执行重定向时,所有换行符都编码为窄ASCII换行符(0d0a)。在PowerShell中执行重定向时,将插入空字符 PS C:\test> .\testu16.exe > p.txt PS C:\test> xxd p.txt 0000000: fffe 6f00 0

在C程序中,我使用wprintf在Windows控制台中打印Unicode(UTF-16)文本。这可以正常工作,但是当程序的输出被重定向到日志文件时,日志文件的UTF-16编码已损坏。 在Windows命令提示符下执行重定向时,所有换行符都编码为窄ASCII换行符(0d0a)。在PowerShell中执行重定向时,将插入空字符

PS C:\test> .\testu16.exe > p.txt
PS C:\test> xxd p.txt
0000000: fffe 6f00 0000 6e00 0000 6500 0000 0d00  ..o...n...e.....
0000010: 0a00 0000 7400 0000 7700 0000 6f00 0000  ....t...w...o...
0000020: 0d00 0a00 0000 7400 0000 6800 0000 7200  ......t...h...r.
0000030: 0000 6500 0000 6500 0000 0d00 0a00 0000  ..e...e.........
0000040: 0d00 0a00                                ....
是否可以将输出重定向到适当的UTF-16日志文件

示例程序:

#include <stdio.h>
#include <windows.h>
#include <fcntl.h>
#include <io.h>

int main () {

  int prevmode;

  prevmode = _setmode(_fileno(stdout), _O_U16TEXT);
  fwprintf(stdout,L"one\n");
  fwprintf(stdout,L"two\n");
  fwprintf(stdout,L"three\n");
  _setmode(_fileno(stdout), prevmode);


  return 0;
}
正在重定向PowerShell中的输出。查看所有插入的文件

PS C:\test> .\testu16.exe > p.txt
PS C:\test> xxd p.txt
0000000: fffe 6f00 0000 6e00 0000 6500 0000 0d00  ..o...n...e.....
0000010: 0a00 0000 7400 0000 7700 0000 6f00 0000  ....t...w...o...
0000020: 0d00 0a00 0000 7400 0000 6800 0000 7200  ......t...h...r.
0000030: 0000 6500 0000 6500 0000 0d00 0a00 0000  ..e...e.........
0000040: 0d00 0a00                                ....
“>”将始终将控制台UTF16重定向为可打印的“ASCII”,即使您在输出上放置了BOM表或使用
prevmode=\u setmode(\u fileno(stdout),\u O\u BINARY)。我在windows7上也有同样的问题,但在fwprintf上无法做到这一点

这个答案是我从你那里得到的。 谢谢你,汉斯

错误的换行是标准输出缓冲的结果。我们需要先刷新流,然后再将模式设置回原始模式

prevmode = _setmode(_fileno(stdout), _O_U16TEXT);
fwprintf(stdout,L"one\n");
fwprintf(stdout,L"two\n");
fwprintf(stdout,L"three\n");
fflush(stdout);               /* flush stream */
_setmode(_fileno(stdout), prevmode);
在命令提示符(cmd.exe)中重定向输出将创建一个正确的UTF-16文件,而不包含BOM表

c:\test>.\testu16 > o.txt

c:\test>xxd o.txt
0000000: 6f00 6e00 6500 0d00 0a00 7400 7700 6f00  o.n.e.....t.w.o.
0000010: 0d00 0a00 7400 6800 7200 6500 6500 0d00  ....t.h.r.e.e...
0000020: 0a00                                     ..
在powershell中,输出仍然错误

PS C:\test> .\testu16 > p.txt
PS C:\test> xxd p.txt
0000000: fffe 6f00 0000 6e00 0000 6500 0000 0d00  ..o...n...e.....
0000010: 0a00 0000 0d00 0a00 0000 7400 0000 7700  ..........t...w.
0000020: 0000 6f00 0000 0d00 0a00 0000 0d00 0a00  ..o.............
0000030: 0000 7400 0000 6800 0000 7200 0000 6500  ..t...h...r...e.
0000040: 0000 6500 0000 0d00 0a00 0000 0d00 0a00  ..e.............
0000050: 0000 0d00 0a00                           ......
这是因为PowerShell不会保持流不变。它试图解释它并将其转换为UTF-16。它猜测输入流编码是ANSI。PowerShell添加了一个UTF-16 BOM,其余为双编码UTF-16。这就解释了额外的零

即使使用out文件并指定编码也无济于事

PS C:\test> .\testu16.exe | out-file p.txt -encoding unicode
PS C:\test> xxd p.txt
0000000: fffe 6f00 0000 6e00 0000 6500 0000 0d00  ..o...n...e.....
0000010: 0a00 0000 0d00 0a00 0000 7400 0000 7700  ..........t...w.
0000020: 0000 6f00 0000 0d00 0a00 0000 0d00 0a00  ..o.............
0000030: 0000 7400 0000 6800 0000 7200 0000 6500  ..t...h...r...e.
0000040: 0000 6500 0000 0d00 0a00 0000 0d00 0a00  ..e.............
0000050: 0000 0d00 0a00                           ......
PowerShell需要了解编码,这是通过首先打印UTF-16 BOM来完成的:

prevmode = _setmode(_fileno(stdout), _O_U16TEXT);
fwprintf(stdout, L"\xfeff");  /* UTF-16LE BOM */
fwprintf(stdout,L"one\n");
fwprintf(stdout,L"two\n");
fwprintf(stdout,L"three\n");
fflush(stdout);               /* flush stream */
_setmode(_fileno(stdout), prevmode);
现在我们得到了一个正确的UTF-16文件

PS C:\test> .\testu16 > p.txt
PS C:\test> xxd p.txt
0000000: fffe 6f00 6e00 6500 0d00 0a00 7400 7700  ..o.n.e.....t.w.
0000010: 6f00 0d00 0a00 7400 6800 7200 6500 6500  o.....t.h.r.e.e.
0000020: 0d00 0a00

显然,第一个示例中的错误是
L“\n”
0d0a00
的形式获得输出,而不是
0d00a00
。不知道第二个例子中的问题是什么;除了BOM表和换行符外,它看起来有点像UTF-32。嗯。@MrLister如果是这样的话,那将是一个编译器错误。您必须检查二进制文件才能确定。OP,你在用什么编译器?昨天给出了正确的答案,但他的答案被删除了,如果你回复我,我会给你赏金的。如果没有,我将发布您的答案。@coladict,我使用的是mingw-w64和Visual Studio 2013。两者都给出相同的结果。@MrLister,第二个示例是双编码UTF-16。PowerShell修改流。见下面我的答案。