有没有办法在 c++ 中确定以字节为单位的 stdin 内容大小?[复制]

Is there any way to determine stdin content size in bytes in c++? [duplicate]

提问人:Alexey104 提问时间:10/5/2020 最后编辑:Robert ColumbiaAlexey104 更新时间:10/5/2020 访问量:405

问:

我是编程新手,我正在尝试为 Linux 编写一个 c++ 程序,该程序将创建一个子进程,而这个子进程将执行一个外部程序。该程序的输出应重定向到主程序并保存到字符串变量中,保留所有空格和换行符。我不知道输出将包含多少行/字符。
这是基本思想:

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
 
int main()
{
    int pipeDescriptors[2];
    pipe(pipeDescriptors);
    pid_t pid = fork();
    if (pid == -1)
    {
        std::cerr << __LINE__ << ": fork() failed!\n" <<
        std::strerror(errno) << '\n';
        return 1;
    }
    else if (!pid)
    {
        // Child process
        close(pipeDescriptors[0]); // Not gonna read from here
        if (dup2(pipeDescriptors[1], STDOUT_FILENO) == -1) // Redirect output to the pipe
        {
            std::cerr << __LINE__ << ": dup2() failed!\n" <<
            std::strerror(errno) << '\n';
            return 1;
        }
        close(pipeDescriptors[1]); // Not needed anymore
        execlp("someExternalProgram", "someExternalProgram", NULL);
    }
    else
    {
        // Parent process
        close(pipeDescriptors[1]); // Not gonna write here
        pid_t stdIn = dup(STDIN_FILENO); // Save the standard input for further usage
        if (dup2(pipeDescriptors[0], STDIN_FILENO) == -1) // Redirect input to the pipe
        {
            std::cerr << __LINE__ << ": dup2() failed!\n" <<
            std::strerror(errno) << '\n';
            return 1;
        }
        close(pipeDescriptors[0]); // Not needed anymore
        int childExitCode;
        wait(&childExitCode);
        if (childExitCode == 0)
        {
            std::string childOutput;
            char c;
            while (std::cin.read(&c, sizeof(c)))
            {
                childOutput += c;
            }
            // Do something with childOutput...
        }
        if (dup2(stdIn, STDIN_FILENO) == -1) // Restore the standard input
        {
            std::cerr << __LINE__ << ": dup2() failed!\n" <<
            std::strerror(errno) << '\n';
            return 1;
        }
        // Some further code goes here...
    }
    return 0;
}

上面代码的问题在于,当函数读取输入流中的最后一个字节时,它实际上并不“知道”这个字节是最后一个字节并尝试进一步读取,这导致了设置和,所以我以后无法再从标准输入中读取。 重置这些标志,但仍然无法使用。std::cin.get()failbiteofbitstd::cinstd::cin.clear()stdin

如果我能在不超出流中最后一个字符的情况下获得内容的精确大小(以字节为单位),我将能够将这个确切的字节数读取到字符串变量中。但我想没有办法做到这一点。
那么我该如何解决这个问题呢?我是否应该使用中间文件将子进程的输出写入其中,然后再从父进程中读取它?
stdinstd::cin.read()

C++ Linux stdin IOstream EOF

评论

1赞 Sam Varshavchik 10/5/2020
不,您不知道 的“精确大小(以字节为单位)”。它可能完全是空的。也可能是《哈利·波特与死亡圣器》的全部内容。直到你阅读它,你才知道。您的 C++ 教科书应该有很多阅读事先不知道大小的文件的示例,以及在不知道其大小的情况下读取其全部内容的技术。你的教科书中有什么你不理解或不清楚的具体内容吗?std::cin
1赞 Botje 10/5/2020
这听起来像是一个 XY 问题:您要求我们解决一个框架问题 (X),因为您正在重用 stdin 从外部进程 (Y) 读取数据。为什么不通过直接从管道读取并保持 stdin 不变来求解 Y?如何从 POSIX 文件描述符构建 c++ fstream?
1赞 anastaciu 10/5/2020
您可以使用 man7.org/linux/man-pages/man2/ioctl.2.html 在 stdin 中查找流的大小。

答:

1赞 Maxim Egorushkin 10/5/2020 #1

子进程写入管道,但父进程在子进程终止之前不会读取管道。如果子项写入的管道缓冲区大小超过管道大小,则会阻塞等待父级读取管道,但父级在等待子级终止时会被阻塞,从而导致死锁。

为了避免这种情况,父进程必须继续读取管道,直到并且只有这样才能用于获取子进程退出状态。EOFwait

例如:

// Read entire child output.
std::string child_stdout{std::istreambuf_iterator<char>{std::cin},
                         std::istreambuf_iterator<char>{}};
// Get the child exit status.
int childExitCode;
if(wait(&childExitCode))
    std::abort(); // wait failed.

您可能还想从管道文件描述符打开一个新的 istream,以避免混乱状态。std::cin

评论

0赞 Alexey104 10/5/2020
谢谢你指出来!我真的很想念它。