有效地将一个标准流复制到另一个标准流

时间:2010-01-18 08:34:02

标签: c++ stream

好的,这里有一些代码概述了我正在尝试做的事情。

#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&amp;)方法将一行写入另一个字符串流,然后我可以输出它。

忽略这一事实,这可能不是从我读过的缓冲区中一次提取一行的最佳方式(虽然我希望任何人提供一个更好的替代我在这里发布的那个),一旦第一个ss.get( *s2.rdbuf() )被调用,ss处于失败状态,我就无法解决原因。输入文件中有大量数据,因此ss肯定会包含多行输入。

有什么想法吗?

2 个答案:

答案 0 :(得分:1)

在我看来,获得体面效率的第一步(也可能是最大步骤)是最小化复制数据。由于您在char []中获得了长度的数据,我的第一个倾向是首先使用该缓冲区创建strstream。然后,不是一次将字符串复制到另一个strstream(或stringstream),而是将一个字符串一次复制到您将用于将它们写入输出的流中。

如果允许修改缓冲区的内容,另一种可能性是通过简单地用'\ 0'替换每个'\ n'来将缓冲区解析成行。如果你要这样做,你通常也想创建一个指向每行开头的指针的向量(deque等)(即找到第一个'\ r'或'\ n',和用'\ 0'替换它。然后,除了'\ r'或'\ n'之外的下一个是下一行的开头,所以它在你的向量中的地址。)

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

答案 1 :(得分:0)

我在Windows上对此进行了测试,因此您可能需要对此进行验证;

如果data.out以换行符开头,我遇到了同样的问题,否则ss.get(* s2.rdbuf())在第一次调用时工作正常。

第二次调用时,流的当前位置没有超过EOL。因此,当第二次调用时,立即尝试读取EOL,并且由于没有复制其他字符,因此会设置失败位。

快速而且可能很脏的修复:

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