Эффективное копирование одного стандартного потока в другой - PullRequest
8 голосов
/ 18 января 2010

Хорошо, вот код, который описывает то, что я пытаюсь сделать.

#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 определенно должно содержать более одной строки ввода.

Есть идеи?

Ответы [ 2 ]

1 голос
/ 18 января 2010

Мне кажется, что первым (и, вероятно, самым большим) шагом к достижению приличной эффективности является минимизация копирования данных. Поскольку вы получаете данные в char [] с длиной, моей первой тенденцией будет начать с создания strstream с использованием этого буфера. Затем вместо того, чтобы копировать строку за раз в другой strstream (или stringstream), я бы копировал строки по одной в поток, который вы будете использовать для записи их в вывод.

Если вам разрешено изменять содержимое буфера, другой возможностью будет разбить буфер на строки, просто заменив каждое '\ n' на '\ 0'. Если вы собираетесь это сделать, вы, как правило, захотите создать вектор (deque и т. Д.) Указателей на начало каждой строки (например, найдите первый '\ r' или '\ n', и замените его на «\ 0». Затем следующая вещь, кроме «\ r» или «\ n», является началом следующей строки, поэтому ее адрес в вашем векторе).

Я бы также подумал о том, можно ли избежать вывода по очереди. Чтение большого буфера для поиска новой строки происходит относительно медленно. Если вы все равно собираетесь в конечном итоге писать одну строку за другой, вы можете избежать всего этого, просто записав весь буфер в выходной поток и покончив с этим.

0 голосов
/ 18 января 2010

Я проверил это в 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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...