2017-02-22 8 views
1

結果を得たいので、プログラムの出力がデキュー構造に分割されます。パイプでリダイレクトされたプログラムから正しい出力を得ることができません。C++(Windows)

問題を説明する:私は、CreateProcessで作成したプログラムの出力をリダイレクトすることに慣れています。私はプログラムの出力を読んで行ごとに処理する必要があります。プログラム自体は便利な形式で出力を提供しますが、名前のないパイプ経由で受信した出力は、部分に表示される速度が遅く、最後の行がどこかに半分になるように配信されます。パイプからのストリームの次の部分は一致して終了しますが、プログラムの構築にはいくつかの問題があります。

プログラムからの出力とパイプから出力される出力の間のこの不一致の原因は何ですか?

EDIT:命題によると user4581301によって私はにstringstreamとのgetlineを使用しようとしたが、パイプへのリダイレクトなしでプログラムから直接COUTは持っていないにもかかわらずラインはまだ半分にカットされているようですこの問題。これは、行がキューのさまざまな要素で分割されるという問題につながります(下のコードを見てください)。

Sample from the console

方法ReadFromPipeは、ループ内で実行されます。

また、メソッドBufferToQueue。

フラッシュに関する@Captain Obvliousさんのコメントに拡大
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(); 
    } 
} 
+2

フラッシュするのを忘れたような匂い。 –

+0

トピックオフ: 'std :: stringstream'にパイプ出力を書き出し、ストリーム上で' std :: getline'を使って読み込むことを検討しましたか? – user4581301

+0

提案に基づいて質問が編集されました。私は、画面上のテキストを送信することがアプリケーションが適切に動作するためには厳密に必要ではないので、フラッシュでさらに試してみませんでした。プログラムの動作をチェックするのは最初の方法でした。私は、文字列がgetlineで分割された後に正しくキューに入れられないということに懸念しています。 – Jendker

答えて

0

:WriteToPipe機能が行末にフラッシュしないので、あなたが直面している問題がある

。以前のReadFromPipeコールに最後の文字としてnewlineがない場合は、前の文字列に追加することで、これを解決することができます。

修正機能:

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; 
     } 
    } 
} 
+0

ありがとうございました。それは私に方向性を持って、問題は、適切に再現された文字列が文字列ストリームの最後に表示されないだけでなく、分割関数の問題かもしれません?私は過去にも問題なく使用していました。 – Jendker

+0

stringstreamがパイプから「ゴミ」を受け取る可能性があるのか​​どうかわかりません。 Splitはstringstreamの最後だけでなく( "if(idx == elems.size()-1)std :: cout <<" stream end ";"でチェックされているだけでなく、他の場所で。それは非常に厄介になっています...私は別の方法で、CreateProcess()と無名のパイプでは、プログラムからの出力を取得しようとする必要がありますか?助言がありますか? – Jendker

+0

私が行ったことは、作成された両端キューの2番目の要素をチェックしていて、場合によっては不正な文字列が配置されているということです(つまり、行が別の場所に分割され、行の末尾が新しい行にプッシュされました)。まだフラッシングに問題はありますか?私が言及したように、それはストリングストリームの終わりだけではない。 – Jendker

0

@indeterminatelyに配列決定からの答えが正しかった、あなたの助けをありがとうございました。問題は、パイプがコピーされたバッファが小さすぎて、それを別のパイプに割り当てることでした。

完全な解決策の助けに基づいて@決定的に出力をキュー構造にプッシュする、多分それは誰かを助けるでしょう。唯一の問題は、開かれたプログラムが決して閉じられず、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 

} 
+0

とにかく、このフラッシングを少し早くして、それが満杯になるまで待たないようにする方法はありますか? CreatePipe関数でパイプのサイズを変更しても変更はありませんので、私の推論は間違っています。関数[FlushFileBuffers](https://msdn.microsoft.com/de-de/library/windows/desktop/aa364439(v = vs.85).aspx)もここでは役に立ちません。 – Jendker

関連する問題