在更改文件描述符1以引用不同的文件后,我应该如何管理:: std :: cout?

Omn*_*ous 6 c++ iostream file-descriptor

我想做dup2(fd, 1); close(fd);::std::cout写入新的fd 1.我怎样才能重置状态,::std::cout所以没什么好笑的?例如,预先冲洗是否足够?或者还有更多事要做吗?

我对同样的事情也很好奇::std::cin.

如果你更改了他们在其下面使用的文件描述符,是否有一种重置这些的标准机制?

要明确的是,我的目标基本上是将我自己的输入和输出重定向到其他地方.我希望不会让这个过程无意中在其父级的stdout上烧掉某些东西,或者试图从其父级的stdin中消耗任何东西.而且我再也不想触摸父母的stdin或stdout了.我想忘记他们曾经存在过.

而且我最特别的是不想无意中将输出发送到我父母在不同文件描述符上使用的同一设备.

我的目标是让cin和cout导致与过程开始时完全不同的地方,并且永远不会以任何方式触及他们过去所处的地方.永远!

Dan*_*man 6

选项 1:设置标准输入和标准输出

根据cppreference.com

默认情况下,所有八个标准 C++ 流都与其各自的 C 流同步。

只要您没有明确调用sync_with_stdio(false),他们就会保持这种状态。这是什么意思?下列:

实际上,这意味着同步的 C++ 流是无缓冲的,并且对 C++ 流的每个 I/O 操作都会立即应用于相应的 C 流的缓冲区。这使得自由混合 C++ 和 CI/O 成为可能。

所以,flush()-ing your cin& coutbefore dup()-ing 它们应该足够了,因为它们应该处于一致的状态。

例如,如果您希望处理文件,则可以使用:

if (freopen("input.txt", "r", stdin) == NULL) {
    // Handle error, errno is set to indicate error
}

if (freopen("output.txt", "w", stdout) == NULL) {
    // Handle error, errno is set to indicate error
}
Run Code Online (Sandbox Code Playgroud)

注 1:设置全局extern FILE * stdinorstdout将不起作用,因为它只是将指针的单个实例更改为指向FILEos的相关结构。在此更改之前的任何时刻复制此指针的任何模块将继续使用旧的FILE. 一个具体的例子是 libc++ 的 for 实现cout,它FILE * stdout在对象的初始化过程中复制到一个私有成员。freopen另一方面,改变FILE操作系统的内部结构以使用新打开的文件,影响任何拥有FILE *它的人。

注意 2:当使用dup()口味(而不是freopen())时,我们正在更改底层的fd,而不是FILE*. 该freopen()方法的作用不止于此。从POSIX

freopen() 函数应首先尝试刷新与流关联的流,就像调用 fflush(stream) 一样。未能成功刷新流将被忽略。如果 pathname 不是空指针,则 freopen() 将关闭与流关​​联的任何文件描述符。未能成功关闭文件描述符将被忽略。应清除流的错误和文件结束指示符。

dup()-ing可能有用,但是,它可能很棘手,因为它不会影响 的其他属性FILE*,包括:字符宽度、缓冲状态、缓冲区、I/O、二进制/文本模式指示器、文件结束状态指示器、错误状态指示器、文件位置指示器和(在 C++17 之后)用于防止数据竞争的可重入锁。

如果可能,我建议使用freopen. 否则,您可以按照您自己描述的步骤操作 ( fflush(), clearerr())。跳过fclose()将是明智的,因为我们将无法FILE通过任何 API 方法重新打开相同的内部。

选项 2:设置 cin 和 cout 的 rdbuf()

其他的方式,就像提出了一些意见,正在取代cin的和cout的基础缓冲区使用rdbuf()

你有什么选择?

  • 文件流:打开ifstream&ofstream并使用它们:

    std::ifstream fin("input.txt");
    if (!fin) {
        // Handle error
    }
    cin.rdbuf(fin.rdbuf());
    
    std::ofstream fout("output.txt");
    if (!fout) {
        // Handle error
    }
    cout.rdbuf(fout.rdbuf());
    
    Run Code Online (Sandbox Code Playgroud)
  • 网络流:使用 boost's boost::asio::ip::tcp::iostream(它源自std::streambuf并因此可以工作):

    boost::asio::ip::tcp::iostream stream("www.boost.org", "http");
    if (!stream) {
        // Handle error
    }
    
    cin.rdbuf(stream.rdbuf());
    cout.rdbuf(stream.rdbuf());
    
    // GET request example
    
    cout << "GET /LICENSE_1_0.txt HTTP/1.0\r\n";
    cout << "Host: www.boost.org\r\n";
    cout << "Accept: */*\r\n";
    cout << "Connection: close\r\n\r\n";
    cout.flush();
    
    std::string response;
    std::getline(cin, response);
    
    Run Code Online (Sandbox Code Playgroud)
  • 自定义流:使用您自己的自定义包装器std::streambuf。请参阅此处的示例。