gpt4 book ai didi

c - 我想将 stdin 解释为二进制文件。为什么 freopen 在 Windows 上失败?

转载 作者:可可西里 更新时间:2023-11-01 09:47:25 25 4
gpt4 key购买 nike

TL;DR:为什么 freopen(NULL, "rb", stdin) 在 Windows 上总是失败?

我正在尝试用 C 语言重新实现一个 base64 编码器,它从 stdin 获取输入并将编码后的等价物输出到 stdout。我有一个问题in my previous post其中 fread 过早地发出 EOF 信号。这是我的主要方法:

int main(void)
{
unsigned char buffer[BUFFER_SIZE];
unsigned char base64_buffer[BASE64_BUFFER];

while (1)
{
TRACE_PUTS("Reading in data from stdin...");
size_t read = fread(buffer, 1, sizeof(buffer), stdin); /* Read the data in using fread(3) */

/* Process the buffer */

TRACE_PRINTF("Amount read: %zu\n", read);
TRACE_PUTS("Beginning base64 encode of buffer");
size_t encoded = base64_encode(buffer, read, base64_buffer, sizeof(base64_buffer));

/* Write the data to stdout */
TRACE_PUTS("Writing data to standard output");
...

if (read < sizeof(buffer))
{
break; /* We reached EOF or had an error during the read */
}
}

if (ferror(stdin))
{
/* Handle errors */
fprintf(stderr, "%s\n", "There was a problem reading from the file.");
exit(1);
}

puts(""); /* Output a newline before finishing */

return 0;
}

本质上,它使用 fread 从 stdin 读取数据,编码为 base64,将其写入 stdout,然后在循环结束时检查是否到达 EOF。

当我将二进制文件的内容通过管道传输到此应用程序的标准输入时,它只会读取文件总字节数的一小部分。例如:

$ cat /bin/echo | my_base64_program >/dev/null # only view the trace output
TRACE: C:/Users/James/Code/c/base64/main.c:23: Reading in data from stdin...
TRACE: C:/Users/James/Code/c/base64/main.c:28: Amount read: 600
TRACE: C:/Users/James/Code/c/base64/main.c:29: Beginning base64 encode of buffer
TRACE: C:/Users/James/Code/c/base64/main.c:43: Writing data to standard output
TRACE: C:/Users/James/Code/c/base64/main.c:23: Reading in data from stdin...
TRACE: C:/Users/James/Code/c/base64/main.c:28: Amount read: 600
TRACE: C:/Users/James/Code/c/base64/main.c:29: Beginning base64 encode of buffer
TRACE: C:/Users/James/Code/c/base64/main.c:43: Writing data to standard output
TRACE: C:/Users/James/Code/c/base64/main.c:23: Reading in data from stdin...
TRACE: C:/Users/James/Code/c/base64/main.c:28: Amount read: 600
TRACE: C:/Users/James/Code/c/base64/main.c:29: Beginning base64 encode of buffer
TRACE: C:/Users/James/Code/c/base64/main.c:43: Writing data to standard output
TRACE: C:/Users/James/Code/c/base64/main.c:23: Reading in data from stdin...
TRACE: C:/Users/James/Code/c/base64/main.c:28: Amount read: 569
TRACE: C:/Users/James/Code/c/base64/main.c:29: Beginning base64 encode of buffer
TRACE: C:/Users/James/Code/c/base64/main.c:43: Writing data to standard output

$ cat /bin/echo | wc -c
28352

如您所见,/bin/echo 有 28352 个字节长,但只有约 2400 个字节被处理。我认为原因是因为 stdin 未被视为二进制文件,因此某些控制字符(如链接帖子的答案中提到的 Control-Z)过早地发出 EOF 信号。

我看了一下 base64 source code看起来他们正在使用 xfreopen(它只是 freopen 的包装器)告诉 fread 将标准输入解释为二进制。所以我在 while 循环之前继续这样做:

if (!freopen(NULL, "rb", stdin))
{
fprintf(stderr, "freopen failed. error: %s\n", strerror(errno));
exit(1);
}

但是,现在我的应用程序总是在此时退出:

$ cat /bin/echo | my_base64_program
freopen failed. error: Invalid argument

那么为什么 freopen 在那个时候失败了,当它适用于 base64 时?如果相关的话,我在 Windows 上将 MinGW-w64 与 GCC 结合使用。

最佳答案

为什么 freopen() 通常会失败

C 标准说:

If filename is a null pointer, the freopen function attempts to change the mode of the stream to that specified by mode, as if the name of the file currently associated with the stream had been used. It is implementation-defined which changes of mode are permitted (if any), and under what circumstances.

据推测,您的实现不允许您尝试进行的更改。例如,在 Mac OS X 上,freopen() 的手册页添加了:

The new mode must be compatible with the mode that the stream was originally opened with:

  • Streams originally opened with mode "r" can only be reopened with that same mode.
  • Streams originally opened with mode "a" can be reopened with the same mode, or mode "w".
  • Streams originally opened with mode ``w'' can be reopened with the same mode, or mode "a".
  • Streams originally opened with mode "r+", "w+", or "a+" can be reopened with any mode.

话虽如此,在 Mac OS X 上(无论如何 b 都是空操作),你会没事的。

为什么 freopen() 在 Windows 上特别失败

但是,您使用的是 Windows。您需要学习如何查找和阅读文档。我使用带有术语“site:msdn.microsoft.com freopen”的 Google 搜索来查找我正在寻找的任何功能。该特定搜索产生了 freopen() 的手册它说:

If path, mode, or stream is a null pointer, or if filename is an empty string, these functions invoke the invalid parameter handler, as described in Parameter Validation. If execution is allowed to continue, these functions set errno to EINVAL and return NULL.

这是记录在案的行为:这也是您所看到的行为。您的系统手册很有帮助。它基本上是在说“你不应该”。

如何修复Windows标准输入法的输入模式

我注意到在我的 answer到你的previous question ,我指向 _setmode():

However, it is more likely that you need _setmode():

_setmode(_fileno(stdin), O_BINARY);

这是在 question 的答案中给出的建议那deamentiaemundi指向。

我顺便注意到 setmode() 的 Microsoft 手册页说:

This POSIX function is deprecated. Use the ISO C++ conformant _setmode instead.

这是一个奇怪的评论,因为 POSIX没有标准化功能setmode()首先。

您可以找到 Microsoft 的 fileno() 文档.它也有关于 POSIX 的介绍(但这次它是准确的;POSIX 确实指定了 fileno() )并建议您引用 _fileno() .

关于c - 我想将 stdin 解释为二进制文件。为什么 freopen 在 Windows 上失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39339164/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com