2010-01-18 55 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'之外的下一行是下一行的开始,所以它的地址在你的向量中)。

我也想想你是否可以避免一次一行的输出。通过大缓冲区来查找换行符相对较慢。如果你最终要写一行又一行,你可以通过写入整个缓冲区到输出流并完成它来避免所有这些。