如何在 C++ 中正确打开和关闭文件?
Posted
技术标签:
【中文标题】如何在 C++ 中正确打开和关闭文件?【英文标题】:How to open and close files properly in C++? 【发布时间】:2021-04-30 01:39:54 【问题描述】:我想打开一些文件,重定向它们的输出,然后回到以前的情况,所以我写道:
int fd = open("test.txt", O_WRONLY | O_CREAT, 0666);
dup(1);
dup2(3, 1);
close(3);
//call my func() to print in std::cout which will write to test.txt
dup2(4, 1);//Close file
我知道默认输入通道在索引 0 中,输出通道(屏幕)在索引 1 中,stderr
在索引 2 中,所以fd
将得到 3,dup
1 将创建另一个指向输出通道的指针index 4,然后我将 screen 替换为我的文件并关闭不需要的文件。
我的代码是正确的,但我是否忘记了其他任何内容,或者可以让它更短/更清晰吗?
最重要的是,如果一行失败,我想返回 false 但这将是很多检查,并且在每一个高级步骤中我们都需要关闭更多。我该如何解决这个问题?
如果我想在失败的情况下让一切恢复到默认状态怎么办?
请注意,我只知道并想使用open,close,dup,dup2
【问题讨论】:
如果有人以这样一种方式运行您的程序,那么初始的open()
将返回 5. 或 6. 或 19. Hilarity 会非常简单。在您拥有更多、更多的控件之前,您无法假设新打开的文件描述符是什么。
@SamVarshavchik 我的教授说 open 将返回最少未使用的索引,在我的情况下为 3
正如我所解释的,我可以轻松地运行您的程序,使第一个打开的文件描述符可以是任何东西。你不会是第一个在 *** 上证明他们的教授不称职的人。
关于是什么造就了一个糟糕的程序员,主要有四个要点:1)忽略编译器的诊断 2)编写不进行错误检查的代码 3)发明和重新发明***而不是使用(批准雇主)工具和图书馆 4)拒绝自我教育和学习使用最合适的工具(“我不知道如何使用斧头,所以我会使用锤子。我如何进行锤子切割?” - 在这里开我的玩笑,不过,也有带刀片的锤子——削片锤)。
【参考方案1】:
最重要的是程序员有责任编写可靠、无故障的代码,这包括彻底的错误检查。错误检查通常会占用大部分代码,尤其是当用户交互是程序的一部分时。但是,如果你想成为/成为一名优秀的程序员,那是没有办法的。
也就是说,很容易(在第一步中)更改您的代码,使其不依赖于特定的文件描述符编号(未经测试)。
int fileFd;
int redirectFd1;
int redirectFd2;
fileFd = open( "test.txt", O_WRONLY | O_CREAT, 0666 );
redirectFd1 = dup( 1 );
redirectFd2 = dup2( fileFd, 1 );
close( fileFd );
//call my func() to print in std::cout which will write to test.txt
dup2( redirectFd1, 1 );//Close file
下一步是添加错误检查。
int fileFd;
int redirectFd1;
int redirectFd2;
if ( ( fileFd = open( "test.txt", O_WRONLY | O_CREAT, 0666 ) ) == -1 )
// open() failed. Do appropriate error handling here...
exit( 1 );
if ( ( redirectFd1 = dup( 1 ) ) == -1 )
// dup() failed. Do appropriate error handling here...
// Since we arrvied here, fileFd *is* open. So we need to close it.
// But redirectFd1 is *not* open
close( fileFd );
exit(1);
if ( ( redirectFd2 = dup2( fileFD, 1 ) ) == -1 )
// dup() failed. Do appropriate error handling here...
// Since we arrvied here, fileFd *and* redirectFd1 *are* open.
// So we need to close them.
// But redirectFd2 is *not* open
close( fileFd );
close( redirectFd1 );
exit(1);
close( fileFd );
//call my func() to print in std::cout which will write to test.txt
if ( dup2( redirectFD1, 1 ) == -1 )
// dup2() failed. Do appropriate error handling here...
close( redirectFd1 );
close( redirectFd1 );
人们可能会争论是否需要检查close( fileFd )
语句中的错误。事实是open()
成功了,所以close()
在这里失败是很不寻常的。人们还可以争论是否需要检查最后一个 dup2()
的错误。
一般来说,我会跟踪打开的文件,并注意在清理程序中出现错误时关闭文件。代码可能如下所示:
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
int fileFd;
bool fileFdIsOpen = false;
int redirectFd1;
bool redirectFd1IsOpen = false;
int redirectFd2;
bool redirectFd2IsOpen = false;
void cleanup();
int main( int argc, char* argv[] )
int stdoutFd = fileno( stdout );
if ( ( fileFd = open( "test.txt", O_WRONLY | O_CREAT, 0666 ) ) == -1 )
// open() failed. Do appropriate error handling here...
exit( 1 );
fileFdIsOpen = true;
if ( ( redirectFd1 = dup( stdoutFd ) ) == -1 )
// dup() failed. Do appropriate error handling here...
cleanup();
exit(1);
redirectFd1IsOpen = true;
if ( ( redirectFd2 = dup2( fileFd, stdoutFd ) ) == -1 )
// dup() failed. Do appropriate error handling here...
cleanup();
exit(1);
redirectFd2IsOpen = true;
close( fileFd );
fileFdIsOpen = false;
//call my func() to print in std::cout which will write to test.txt
if ( dup2( redirectFd1, stdoutFd ) == -1 )
// dup2() failed. Do appropriate error handling here...
cleanup();
cleanup();
exit (0);
void cleanup()
if ( fileFdIsOpen )
close( fileFd );
fileFdIsOpen = false;
if ( redirectFd1IsOpen )
close( redirectFd1 );
redirectFd1IsOpen = false;
if ( redirectFd2IsOpen )
close( redirectFd2 );
redirectFd2IsOpen = false;
【讨论】:
对不起,但我认为你的代码不起作用,以防第一次打开失败,为什么你要关闭默认输出流,即使我以后需要它? 另外,你忘了做:redirectFd1的关闭 我不关闭默认输出流。我在错误路径中添加了一些注释。 注意 if-then 中的exit(1);
。发生错误后程序停止。
在程序结束时关闭redirectFd1
不会造成任何伤害。程序消失了,无论如何内核都会清理,包括关闭打开的文件描述符。【参考方案2】:
我知道默认输入通道在索引 0 中,输出通道 (screen) 在索引 1 和 stderr 在索引 2 所以 fd 将得到 3
这是未确定的,进程可以运行在那些 id 将被更改并且起始描述符也可能与 stderr+1 不同的情况下。此外,在 Linux 上,有一些情况下可以在没有打开的情况下创建进程,但这是不同的。
在 C 头文件 <stdio.h>
中定义了那些应该由运行时库初始化的变量。
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;
您展示的代码几乎是 C 代码,而不是 C++,但您可以使用 <cstdio>
和 fileno
函数来获取描述符
int fileno(FILE *stream);
您必须检查fileno
、dup
和open
的返回结果,以确保您的程序没有遇到强加于它的某些限制,而errno
变量会告诉您原因。在其他一些平台上,例如 Windows,确实存在更合适的错误报告功能。
【讨论】:
以上是关于如何在 C++ 中正确打开和关闭文件?的主要内容,如果未能解决你的问题,请参考以下文章