如何在 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 头文件 &lt;stdio.h&gt; 中定义了那些应该由运行时库初始化的变量。

   extern FILE *stdin;
   extern FILE *stdout;
   extern FILE *stderr;

您展示的代码几乎是 C 代码,而不是 C++,但您可以使用 &lt;cstdio&gt;fileno 函数来获取描述符

   int fileno(FILE *stream);

您必须检查filenodupopen 的返回结果,以确保您的程序没有遇到强加于它的某些限制,而errno 变量会告诉您原因。在其他一些平台上,例如 Windows,确实存在更合适的错误报告功能。

【讨论】:

以上是关于如何在 C++ 中正确打开和关闭文件?的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中打开特定应用程序后如何关闭它?

如何正确关闭DLL及其资源

如何在 Mac 上打开 PhpMyAdmin? [关闭]

C++:命名空间——如何在头文件和源文件中正确使用?

C++中如何同时运行俩个dos命令?

“C++”如何打开用变量命名的文件?