高效地将一个std流复制到另一个流

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对象中,以便可以逐行删除它。
我想使用stringstream上的get( streambuf& )方法将一行写入另一个stringstream,然后输出它。
忽略从已读取的缓冲区中提取每行的最佳方法可能不是这种方法(尽管我希望任何人都能提供比我发布的更好的替代方案),但只要第一个ss.get(*s2.rdbuf())被调用,ss就处于失败状态,我无法弄清楚原因。输入文件中有大量数据,因此ss肯定包含多行输入。
有什么想法吗?
2个回答

1

我认为要获得良好的效率,第一步(可能也是最重要的一步)就是尽量减少数据复制。由于您使用 char [] 接收数据,因此我的第一反应是使用该缓冲区创建一个 strstream。然后,不要将字符串一次又一次地复制到另一个 strstream(或 stringstream)中,而是将字符串逐个复制到您将用于写入输出的流中。

如果允许修改缓冲区的内容,则可以通过将每个 '\n' 替换为 '\0' 将缓冲区解析为行。如果您要这样做,通常还需要创建指向每行开头的指针的向量(deque 等)。即找到第一个 '\r' 或 '\n',并将其替换为 '\0'。然后,除了 '\r' 或 '\n' 之外的下一件事情就是下一行的开头,因此它在您的向量中的地址就是下一行的开头。

我还会仔细考虑是否可以避免逐行输出。阅读大型缓冲区以查找换行符相对较慢。如果最终您将一个接一个地写入一行,您可以通过将整个缓冲区直接写入输出流并完成它来避免所有这些麻烦。


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();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接