简体   繁体   English

无法从通过管道C ++重定向的程序获得正确的输出(Windows)

[英]Cannot get correct output from program redirected via pipe C++ (Windows)

I would like to achieve a result, so that the output of the program goes splitted to the deque structure. 我想实现一个结果,以便程序的输出分裂为deque结构。

To describe to problem: I am dealong with with the redirecting the output of the program created with CreateProcess. 描述问题:我正在处理重定向用CreateProcess创建的程序的输出。 I need to read the output of program and process it line by line. 我需要读取程序的输出并逐行处理它。 The program itself provides the output in the convenient form, but the output which I receive via unnamed pipe is delivered slower (with smaller frequency) they appear in the portions and in such a way that the last line is somewhere cut in half. 程序本身以方便的形式提供输出,但是我通过未命名的管道接收的输出传送速度较慢(频率较小)它们出现在部分中,并且最后一行在某处减少了一半。 The next portion of the stream from the pipe will match and finish the line, but it will make some problems with the construction of the program. 来自管道的下一部分流将匹配并完成该行,但是它会对程序的构造产生一些问题。

What be a source of this discrepancy betweenn the cout from the program and the output redirected throught the pipe? 程序中的cout与通过管道重定向的输出之间存在这种差异的根源是什么?

EDIT: According to the proposition by user4581301 I've tried to use the stringstream and getline, but it seems that the lines are still cut in half even though the direct cout from the program without redirection to pipe does not have this problem. 编辑:根据user4581301的提议,我试图使用stringstream和getline,但似乎线仍然被切成两半,即使程序的直接cout没有重定向到管道没有这个问题。 This leads to the issue, that the lines are splitted in the different elements of the queue (please look at the code below). 这导致了这样的问题:线条在队列的不同元素中被分割(请查看下面的代码)。

Sample from the console 来自控制台的示例

The method ReadFromPipe is run in the loop. ReadFromPipe方法在循环中运行。

void ProcessManagement::ReadFromPipe(void)

// Read output from the child process's pipe for STDOUT

{
    DWORD dwRead, dwWritten;
    char buffer[BUFSIZE];
    BOOL bSuccess = FALSE;
    std::deque<std::deque<std::string>> elems;

while (ReadFile(g_hChildStd_OUT_Rd, buffer, sizeof(buffer)-1, &dwRead, NULL) != FALSE)
{
    /* add terminating zero */
    buffer[dwRead] = '\0';
    std::stringstream streamBuffer;
    streamBuffer << buffer;
    BufferToQueue(streamBuffer, ' ', elems);

    // Print deque
    for (std::deque <std::string> a : elems)
    {
        for (std::string b : a)
            std::cout << b;
        std::cout << std::endl;
    }

    }
}

And the method BufferToQueue. 和方法BufferToQueue。

void ProcessManagement::BufferToQueue(std::stringstream &streamBuffer, char delim, std::deque<std::deque<std::string>> &elems) {
    std::string line;
    std::string word;
    std::deque<string> row;

 // Splitting the stringstream into queue of queue (does not work properly)
    while (std::getline(streamBuffer, line))
    {
        std::istringstream iss(line);
        while (std::getline(iss, word, delim))
            row.push_back(word);
    elems.push_back(row);
    row.clear();
    }
}

Expanding on @Captain Obvlious's comment regarding flush: 扩展@Captain Obvlious关于同花顺的评论:

The problem you are facing is because the WriteToPipe function does not flush at end of line. 您遇到的问题是因为WriteToPipe函数不会在行尾刷新。 You can fix this in the reader by ensuring that you append to the previous string if the the previous ReadFromPipe call did not have a newline as the last character. 如果前一个ReadFromPipe调用没有newline作为最后一个newline ,则可以通过确保附加到上一个字符串来解决此问题。

Modified functions: 修改功能:

bool ProcessManagement::BufferToQueue(std::stringstream &streamBuffer, char     delim, std::deque<std::deque<std::string>> &elems)
{
    std::string line;
    std::string word;
    std::deque<string> row;

    bool is_unflushed_line = streamBuffer.str().back() != '\n';

    // Splitting the stringstream into queue of queue (does not work properly)
    while (std::getline(streamBuffer, line))
    {
        std::istringstream iss(line);
        while (std::getline(iss, word, delim)) {
            row.push_back(word);
        }
        elems.push_back(row);
        row.clear();
    }
    return is_unflushed_line;
}

void ProcessManagement::ReadFromPipe(void)
// Read output from the child process's pipe for STDOUT
{
    DWORD dwRead, dwWritten;
    char buffer[BUFSIZE];
    BOOL bSuccess = FALSE;
    std::deque<std::deque<std::string>> elems;

    while (ReadFile(g_hChildStd_OUT_Rd, buffer, sizeof(buffer)-1, &dwRead, NULL) != FALSE)
    {
        /* add terminating zero */
        buffer[dwRead] = '\0';
        std::stringstream streamBuffer;
        streamBuffer << buffer;
        bool is_unflushed_line = BufferToQueue(streamBuffer, ' ', elems);

        for(auto idx = 0; idx != elems.size(); ++idx)
        {
            for (std::string const& b : elems[idx])
                std::cout << b;
            if(idx == elems.size() - 1 && is_unflushed_line)
                break;// don't print a newline if input did not end with a newline
            std::cout << std::endl;
        }
    }
}

The answer from @indeterminately sequenced was correct, thank you for your help. @indeterminately排序的答案是正确的,谢谢你的帮助。 The problem was, that the buffer to which the pipe was copied was too small and it was deviding it to the seperate ones. 问题是,管道被复制到的缓冲区太小而且将它分配给单独的管道。

The complete solution based on the help of @indeterminately sequenced to push the output to queue structure, maybe it will help someone. 基于@indeterminately排序的完整解决方案将输出推送到队列结构,也许它会帮助某人。 The only problem is that the opened program is never closed, the TerminateProcess function has to be used somewhere. 唯一的问题是打开的程序永远不会关闭,TerminateProcess函数必须在某处使用。

void ProcessManagement::CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
    SECURITY_ATTRIBUTES saAttr = { sizeof(SECURITY_ATTRIBUTES) };

    saAttr.bInheritHandle = TRUE;   //Pipe handles are inherited by child process.
    saAttr.lpSecurityDescriptor = NULL;

    // Create a pipe to get results from child's stdout.
    if (!CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0))
        return;

    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    si.hStdOutput = hPipeWrite;
    si.hStdError = hPipeWrite;
    si.wShowWindow = SW_HIDE;       // Prevents cmd window from flashing. Requires STARTF_USESHOWWINDOW in dwFlags.

    std::string command_temp = (" -i \"LAN 3\"");
    LPSTR st = const_cast<char *>(command_temp.c_str());

    BOOL fSuccess = CreateProcessA(program_path.c_str(), st, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
    if (!fSuccess)
    {
        CloseHandle(hPipeWrite);
        CloseHandle(hPipeRead);
        return ;
    }

    /* Needs to be used somewhere
    TerminateProcess(pi.hProcess,exitCode);
    CloseHandle(hPipeWrite);
    CloseHandle(hPipeRead);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);*/
}

void ProcessManagement::ReadFromPipe(void)
// Read output from the child process's pipe for STDOUT
{
    std::deque<std::deque<std::string>> elems;

    // Give some timeslice (50ms), so we won't waste 100% cpu.
    bProcessEnded = WaitForSingleObject(pi.hProcess, 50) == WAIT_OBJECT_0;

    // Even if process exited - we continue reading, if there is some data available over pipe.
    for (;;)
    {
        char buf[8192];
        DWORD dwRead = 0;
        DWORD dwAvail = 0;

        if (!::PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL))
            break;

        if (!dwAvail) // no data available, return
            break;
        if (!::ReadFile(hPipeRead, buf, min(sizeof(buf)-1, dwAvail), &dwRead, NULL) || !dwRead)
            // error, the child process might ended
            break;
        buf[dwRead] = '\0';
        std::stringstream streamBuffer;
        streamBuffer << unflushed_line << buf; // add to buffer also last unflashed line
        unflushed_line = BufferToQueue(streamBuffer, ' ', elems);

        for (auto idx = 0; idx != elems.size(); ++idx)
        {
            for (std::string const& b : elems[idx])
                std::cout << b;

            std::cout << std::endl;
        }

    }
}

std::string ProcessManagement::BufferToQueue(std::stringstream &streamBuffer, char delim, std::deque<std::deque<std::string>> &elems) {
    std::string line;
    std::string word;
    std::deque<string> row;

    bool is_unflushed_line = streamBuffer.str().back() != '\n';

    // Splitting the stringstream into queue of queue (does not work properly)
    while (std::getline(streamBuffer, line, '\n'))
    {
        std::istringstream iss(line);
        while (std::getline(iss, word, delim)) {
            row.push_back(word);
        }
        elems.push_back(row);
        row.clear();
    }

    if (is_unflushed_line)
    {
        elems.pop_back(); // pop not fully flushed line
    }
    else line.clear(); // if the line was fully flushed return empty string

    return line; // to add to buffer for next push to queue if the last was not flushed at the end

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM