【问题标题】:fwrite with non ASCII charactersfwrite 使用非 ASCII 字符
【发布时间】:2015-06-21 06:01:39
【问题描述】:

考虑以下程序:

#include <stdio.h>
#include <string.h>

int main() {
  char* alpha = "Ω";
  fwrite(alpha, 1, strlen(alpha), stdout);
  return 0;
}

在 Windows 上,我得到以下输出:

��

我尝试将行更改为:

char* alpha = "zΩ";

并且打印正确。输出编码正确,只是不打印 正确:

$ 坏 | od-tx1c 0000000 ce a9 316 251 $ 好 | od-tx1c 0000000 7a ce a9 z 316 251

如何使用 fwrite 以非 ASCII 作为第一个字符?

回复部分cmets:源文件正确格式化为UTF-8,我的代码页也正确设置为UTF-8:

$ chcp.com 活动代码页:65001

【问题讨论】:

  • 调用_setmode(_fileno(stdout), _O_U16TEXT),然后使用fwrite写一个wchar_t *宽字符串。由于底层 CRT 文件是 UTF-16 模式,并且是一个控制台,所以写入是通过调用 Unicode API WriteConsoleW 来实现的。
  • 这对我有用:wchar_t *alpha = L"Ω"; _setmode(_fileno(stdout), _O_U16TEXT); fwrite(alpha, 2, wcslen(alpha), stdout);
  • {0xa9, 0x03} 是“Ω”的小端 UTF-16,即 U+03A9。您是使用普通的 Windows 控制台来运行它还是在 POSIX shell 中运行,该 shell 管道连接到某种 pty 实现?检查GetFileType(GetStdHandle(STD_OUTPUT_HANDLE))。是FILE_TYPE_CHAR(2,控制台缓冲区句柄)还是FILE_TYPE_PIPE(3)?
  • 好吧,如果它调用WriteFile 而不是WriteConsoleW,那么这将不起作用。对不起。如果_setmode 成功,请尝试调用wprintffwprintf
  • 我认为在这种情况下,将 Ω 写为 UTF-8 字符串 "\xce\xa9" 的问题在于,fwrite 在写入第一个字节时会刷新其缓冲区。据我所知,仅东亚语言环境中的 DBCS 支持跨写入拆分多字节字符串,而不支持 UTF-8。 (实际上,当语言环境不是默认的 C 语言环境时,Microsoft 的 CRT 对 DBCS 有特殊支持。)因此控制台尝试将 "\xce""\xa9" 解码为两个单独的 UTF-8 字符串。如果您添加一个初始的"z" 字节,那么"\xce\xa9" 可能会在对WriteFile 的一次调用中写入。

标签: c windows fwrite non-ascii-characters


【解决方案1】:

在 Windows 上,fwrite 在内部调用 WriteFile,在这种情况下是错误的。我的 解决方案是直接致电WriteFile

#include <windows.h>

int main() {
  char* alpha = "Ω";
  DWORD bravo;
  WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), alpha, strlen(alpha), &bravo, 0);
  return 0;
}

【讨论】:

  • 一般你需要lpNumberOfBytesWritten。如果标准输出是管道怎么办?例如,“[当]写入缓冲区空间不足的非阻塞字节模式管道句柄时,WriteFile 返回 TRUE*lpNumberOfBytesWritten &lt; nNumberOfBytesToWrite”。但是,当目标是控制台缓冲区时,lpNumberOfBytesWritten 仅在 Windows 8+ 中是正确的。在 Windows 7 中,它错误地返回写入的 UTF-16 代码的数量。因此,您需要单独处理 stdout 是控制台的情况,在这种情况下您可能可以忽略 lpNumberOfBytesWritten
  • 如果您仍然需要单独的控制台代码路径,您也可以自己将 UTF-8 解码为 UTF-16 并调用 WriteConsoleW 。当 stdout 不是控制台时,您可以使用 fwrite
  • 我必须强调控制台中的代码页 65001 已损坏。尝试将非 ASCII Unicode 粘贴到控制台并尝试通过 ReadFile 读取它。它使用WideCharToMultiByte 使用针对ANSI 大小的临时缓冲区编码到控制台代码页,即每个wchar_t 字符1 个字节。这对于给定非 ASCII 字符的 UTF-8 失败。但是 conhost.exe 并没有通过调整临时缓冲区的大小来处理这个问题。它只是忽略失败并返回它“成功”读取 0 个字节。你对此无能为力。即使是 Windows 10 也有这个 bug。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-06
  • 1970-01-01
  • 1970-01-01
  • 2011-06-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多