2010-01-18 78 views
8

好的,下面是一些代碼,概述了我想要做的事情。有效地將一個標準流複製到另一個標準流

#include <sys/types.h> 
#include <sys/stat.h> 
#include <sys/fcntl.h> 

#include <iostream> 
#include <sstream> 

int main(int c, char *v[]) 
{ 
    int fd = open("data.out", O_RDONLY | O_NONBLOCK); 
    std::cout << "fd = " << fd << std::endl; 

    char buffer[ 1024000 ]; 
    ssize_t nread; 

    std::stringstream ss; 

    while(true) 
    { 
     if ((nread = read(fd, buffer, sizeof(buffer) - 1)) < 0) 
      break; 

     ss.write(buffer, nread); 

     while(true) 
     { 
      std::stringstream s2; 

      std::cout << "pre-get : " << 
       (((ss.rdstate() & std::ios::badbit) == std::ios::badbit) ? "bad" : "") << " " << 
       (((ss.rdstate() & std::ios::eofbit) == std::ios::eofbit) ? "eof" : "") << " " << 
       (((ss.rdstate() & std::ios::failbit) == std::ios::failbit) ? "fail" : "") << " " << 
       std::endl; 

      ss.get(*s2.rdbuf()); 

      std::cout << "post-get : " << 
       (((ss.rdstate() & std::ios::badbit) == std::ios::badbit) ? "bad" : "") << " " << 
       (((ss.rdstate() & std::ios::eofbit) == std::ios::eofbit) ? "eof" : "") << " " << 
       (((ss.rdstate() & std::ios::failbit) == std::ios::failbit) ? "fail" : "") << " " << 
       std::endl; 

      unsigned int linelen = ss.gcount() - 1; 

      if (ss.eof()) 
      { 
       ss.str(s2.str()); 
       break; 
      } 
      else if (ss.fail()) 
      { 
       ss.str(""); 
       break; 
      } 
      else 
      { 
       std::cout << s2.str() << std::endl; 
      } 
     } 
    } 
} 

它首先將大塊數據讀入數據緩衝區。我知道有更好的C++方法來完成這一部分,但在我的真實應用程序中,我傳遞了一個char []緩衝區和一個長度。

然後,我將緩衝區寫入std :: stringstream對象,以便我可以一次刪除一行。

我想我會使用字符流中的get(streambuf &)方法將一行寫入另一個字符串流,然後我可以輸出它。

忽略這樣的事實,這可能不是從緩衝區中一次提取一行的最佳方法我已閱讀過(儘管我希望任何人都可以提供一個更好的替代方案來發布此處發佈的內容) ,只要第一個被稱爲ss處於失敗狀態,我不能解決原因。輸入文件中有大量數據,因此ss應該明確包含多行輸入。

任何想法?

回答

0

我已經在Windows上測試過了,所以你可能想驗證一下;

如果data.out以換行符開始,那麼我得到的問題相同,否則ss.get(* s2.rdbuf())對第一次調用可以正常工作。

當第二次調用時,流的當前位置沒有超過EOL。因此,第二次調用get立即嘗試讀取EOL並且由於沒有其他字符已被複制,它會設置失敗位。

快速,也許骯髒的解決辦法:

ss.get(*s2.rdbuf()); 
// Get rid of EOL (may need an extra if file contains both \r and \n) 
ss.get(); 
1

在我看來,第一(可能最大)步驟獲得不俗的效率,儘量減少複製數據。由於你被賦予了一個長度爲char []的數據,我的第一個趨勢是從創建使用該緩衝區的strstream開始。然後,而不是一次複製一個字符串到另一個strstream(或stringstream),我會一次將一個字符串複製到您將用於將它們寫入輸出的流。

如果允許修改緩衝區的內容,另一種可能性是通過用'\ 0'替換每個'\ n'來將緩衝區解析爲行。如果你打算這麼做的話,你通常需要創建一個指向每行開始的指針的向量(deque等)(即找到第一個'\ r'或'\ n',用'\ 0'替換它,然後,除了'\ r'或'\ n'之外的下一行是下一行的開始,所以它的地址在你的向量中)。

我也想想你是否可以避免一次一行的輸出。通過大緩衝區來查找換行符相對較慢。如果你最終要寫一行又一行,你可以通過寫入整個緩衝區到輸出流並完成它來避免所有這些。