【问题标题】:Copying one std stream to another efficiently有效地将一个标准流复制到另一个标准流
【发布时间】:2011-01-06 06:47:24
【问题描述】:

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

#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.get( *s2.rdbuf() ) 被调用,ss 就处于失败状态,我不知道为什么。输入文件中有大量数据,所以ss 肯定包含不止一行输入。

有什么想法吗?

【问题讨论】:

    标签: c++ stream


    【解决方案1】:

    我已在 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();
    

    【讨论】:

      【解决方案2】:

      在我看来,获得良好效率的第一步(可能也是最大的一步)是尽量减少复制数据。由于您在 char[] 中获得了具有长度的数据,因此我的第一个趋势是首先使用该缓冲区创建 strstream。然后,我不会一次将一个字符串复制到另一个 strstream(或 stringstream),而是一次将一个字符串复制到您将用来将它们写入输出的流中。

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-12-14
        • 2011-10-17
        • 2015-06-01
        • 2018-12-13
        • 2010-09-18
        • 1970-01-01
        • 2021-03-09
        相关资源
        最近更新 更多