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



我想做dup2(fd, 1); close(fd);,并让::std::cout写信给新的fd 1。如何重置::std::cout的状态,这样就不会有什么好笑的?例如,事先冲洗是否足够?还是还有比这更多的事情要做?

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

如果您更改它们在它们下面使用的文件描述符,是否有重置这些内容的标准机制?

需要明确的是,我在这里的目标基本上是将我自己的输入和输出重定向到其他地方。我不想让这个过程无意中在其父级的 stdout 上打嗝或试图消耗其父级标准输出中的任何内容。我再也不想碰我父母的 stdin 或 stdout。我想忘记它们曾经存在过。

而且我尤其不想无意中将输出发送到我父母在不同的文件描述符上使用的同一设备。

我的目标是让cin和cout导致与过程开始时完全不同的地方,并且永远不要以任何方式接触他们曾经领导的地方。曾!

选项 1:设置 stdin & stdout

根据 cppreference.com:

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

只要你没有明确地打电话给sync_with_stdio(false),他们就会保持这种状态。什么意思?以下:

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

因此,在dup()之前flush()cincout应该就足够了,因为它们应该处于一致的状态。

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

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
}

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

注2:当使用dup()风格(而不是freopen())时,我们正在更改底层fd,而不是FILE*freopen()方法的作用远不止于此。来自POSIX:

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

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

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

选项 2:设置 cin's & cout 的 rdbuf()

相反,就像一些评论所提议的那样,是使用rdbuf()替换cincout的底层缓冲区。

你有什么选择?

  • 文件流:打开ifstreamofstream并使用它们:

    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());
    
  • 网络流:使用 boost 的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.0rn";
    cout << "Host: www.boost.orgrn";
    cout << "Accept: */*rn";
    cout << "Connection: closernrn";
    cout.flush();
    std::string response;
    std::getline(cin, response);
    
  • 自定义
  • 流:使用您自己的自定义包装器进行std::streambuf。请参阅此处的示例。

您可以为socket_streambuf类创建(或使用现有的)库,并将其与 std::cout/std::cin 相关联:

socket_streambuf<char> buffer{ "127.0.0.1:8888" }; // will call socket(), connect() or throw on failure
std::cout.rdbuf(&buffer); // re-direct cout to the network connection
std::cout << "Hello, World!n"; // may call send() on basic_streambuf::overflow()

这样,您就不必为操作(全局)C 流缓冲区的状态而烦恼。

相关内容

最新更新