检查 FILE* 是否为标准输出;便携的?
check if FILE* is stdout; portable?
我目前正在编写一段代码,其用途是这样的:
program input.txt output.txt
或
program input.txt
在这种情况下它默认为 stdout
。
这是我现在的代码(在main()
内):
FILE *outFile;
if (argc < 3) {
outFile = stdout;
} else {
fprintf(stdout, "Will output to file %s\n", argv[2]);
outFile = fopen(argv[2], "w");
if (outFile == NULL) {
fprintf(stderr, "ERR: Could not open file %s. Defaulting to stdout\n", argv[2]);
outFile = stdout;
}
}
/* ... write stuff to outFile... */
if (argc < 3 && outFile != stdout) {
fclose(outFile);
}
这些是我的顾虑:首先,提供时是否会成功打开和关闭outFile?另外,这会成功 而不是 关闭标准输出吗?如果我关闭 stdout 会发生什么不好的事情吗?
另外,这个是便携的吗?我使用 gcc
进行编译,但该项目将由一位教授使用 Windows.
进行评估
抱歉,如果这个问题有点乱。我来自Python,不是CS专业的(我学的是数学)。
是的,便携,没问题。
是的,它是便携式的。您分配了 outfile = stdout
,因此只要您不在程序的其他地方重新分配它们中的任何一个,它们就会相等。
您实际上也不需要 argc < 3
测试——这两个条件应该始终相同,因为您只在条件为真时才进行赋值。
在任何将重要数据写入 stdout
的程序中,您 应该 在退出前立即关闭 stdout
,以便您可以检查和报告延迟写入错误。 (延迟写入错误是设计错误;fclose
或 close
应该不可能失败。但我们坚持使用它们。)
通常的构造是,在 main
、
的最后
if (ferror(stdout) || fclose(stdout)) {
perror("stdout: write error");
return 1;
}
return 0;
有些程序也会在其中插入 fflush
,但 ISO C 需要 fclose
才能执行 fflush
,因此 不应该 是必要的。这个构造是完全可移植的。
重要的是,这是您在退出前做的最后一件事。图书馆通常认为 stdout
永远不会关闭,因此如果您在关闭 stdout
后调用它们,它们可能会出现故障。 stdin
和 stderr
这样也很麻烦,但我还没有遇到 想要 关闭它们的情况。
有时确实会发生您想在程序完全完成之前关闭 stdout
的情况。在那种情况下,您实际上应该让 FILE
保持打开状态,但关闭底层 "file descriptor" 并将其替换为虚拟对象。
int rfd = open("/dev/null", O_WRONLY);
if (rfd == -1) perror_exit("/dev/null");
if (fflush(stdout) || close(1)) perror_exit("stdout: write error");
dup2(rfd, 1);
close(rfd);
此构造不可移植到 Windows。有一个等价物,但我不知道它是什么。它也不是线程安全的:另一个线程可以在 close
和 dup2
操作之间调用 open
并被分配 fd 1,或者它可以尝试向 stdout
写入一些东西在那个 window 并得到一个虚假的写入错误。为了线程安全,您必须复制旧的 fd 1 并通过该句柄关闭它:
// These allocate new fds, which can always fail, e.g. because
// the program already has too many files open.
int new_stdout = open("/dev/null", O_WRONLY);
if (new_stdout == -1) perror_exit("/dev/null");
int old_stdout = dup(1);
if (old_stdout == -1) perror_exit("dup(1)");
flockfile(stdout);
if (fflush(stdout)) perror_exit("stdout: write error");
dup2 (new_stdout, 1); // cannot fail, atomically replaces fd 1
funlockfile(stdout);
// this close may receive delayed write errors from previous writes
// to stdout
if (close (old_stdout)) perror_exit("stdout: write error");
// this close cannot fail, because it only drops an alternative
// reference to the open file description now installed as fd 1
close (new_stdout);
操作顺序很关键:open
、dup
和 fflush
调用必须在 dup2
调用之前发生,两个 close
调用都必须发生在 dup2
调用之后,stdout 必须从 fflush
调用之前一直锁定到 dup2
调用之后。
其他可能的并发症,作为练习处理:
- 当您不想在出错时停止整个程序时,清理临时 fds 和锁定错误
- 如果线程可能在操作中途被取消
- 如果并发线程可能会调用
fork
和 execve
中间操作
我目前正在编写一段代码,其用途是这样的:
program input.txt output.txt
或
program input.txt
在这种情况下它默认为 stdout
。
这是我现在的代码(在main()
内):
FILE *outFile;
if (argc < 3) {
outFile = stdout;
} else {
fprintf(stdout, "Will output to file %s\n", argv[2]);
outFile = fopen(argv[2], "w");
if (outFile == NULL) {
fprintf(stderr, "ERR: Could not open file %s. Defaulting to stdout\n", argv[2]);
outFile = stdout;
}
}
/* ... write stuff to outFile... */
if (argc < 3 && outFile != stdout) {
fclose(outFile);
}
这些是我的顾虑:首先,提供时是否会成功打开和关闭outFile?另外,这会成功 而不是 关闭标准输出吗?如果我关闭 stdout 会发生什么不好的事情吗?
另外,这个是便携的吗?我使用 gcc
进行编译,但该项目将由一位教授使用 Windows.
抱歉,如果这个问题有点乱。我来自Python,不是CS专业的(我学的是数学)。
是的,便携,没问题。
是的,它是便携式的。您分配了 outfile = stdout
,因此只要您不在程序的其他地方重新分配它们中的任何一个,它们就会相等。
您实际上也不需要 argc < 3
测试——这两个条件应该始终相同,因为您只在条件为真时才进行赋值。
在任何将重要数据写入 stdout
的程序中,您 应该 在退出前立即关闭 stdout
,以便您可以检查和报告延迟写入错误。 (延迟写入错误是设计错误;fclose
或 close
应该不可能失败。但我们坚持使用它们。)
通常的构造是,在 main
、
if (ferror(stdout) || fclose(stdout)) {
perror("stdout: write error");
return 1;
}
return 0;
有些程序也会在其中插入 fflush
,但 ISO C 需要 fclose
才能执行 fflush
,因此 不应该 是必要的。这个构造是完全可移植的。
重要的是,这是您在退出前做的最后一件事。图书馆通常认为 stdout
永远不会关闭,因此如果您在关闭 stdout
后调用它们,它们可能会出现故障。 stdin
和 stderr
这样也很麻烦,但我还没有遇到 想要 关闭它们的情况。
有时确实会发生您想在程序完全完成之前关闭 stdout
的情况。在那种情况下,您实际上应该让 FILE
保持打开状态,但关闭底层 "file descriptor" 并将其替换为虚拟对象。
int rfd = open("/dev/null", O_WRONLY);
if (rfd == -1) perror_exit("/dev/null");
if (fflush(stdout) || close(1)) perror_exit("stdout: write error");
dup2(rfd, 1);
close(rfd);
此构造不可移植到 Windows。有一个等价物,但我不知道它是什么。它也不是线程安全的:另一个线程可以在 close
和 dup2
操作之间调用 open
并被分配 fd 1,或者它可以尝试向 stdout
写入一些东西在那个 window 并得到一个虚假的写入错误。为了线程安全,您必须复制旧的 fd 1 并通过该句柄关闭它:
// These allocate new fds, which can always fail, e.g. because
// the program already has too many files open.
int new_stdout = open("/dev/null", O_WRONLY);
if (new_stdout == -1) perror_exit("/dev/null");
int old_stdout = dup(1);
if (old_stdout == -1) perror_exit("dup(1)");
flockfile(stdout);
if (fflush(stdout)) perror_exit("stdout: write error");
dup2 (new_stdout, 1); // cannot fail, atomically replaces fd 1
funlockfile(stdout);
// this close may receive delayed write errors from previous writes
// to stdout
if (close (old_stdout)) perror_exit("stdout: write error");
// this close cannot fail, because it only drops an alternative
// reference to the open file description now installed as fd 1
close (new_stdout);
操作顺序很关键:open
、dup
和 fflush
调用必须在 dup2
调用之前发生,两个 close
调用都必须发生在 dup2
调用之后,stdout 必须从 fflush
调用之前一直锁定到 dup2
调用之后。
其他可能的并发症,作为练习处理:
- 当您不想在出错时停止整个程序时,清理临时 fds 和锁定错误
- 如果线程可能在操作中途被取消
- 如果并发线程可能会调用
fork
和execve
中间操作