8 Stimmen

Effizientes Kopieren eines std-Streams in einen anderen

Ok, Hier ist einige Code, der skizziert, was ich versuche zu tun.

#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;
            }
        }
    }
}

Zunächst werden große Datenpakete in einen Datenpuffer eingelesen. Ich weiß, es gibt bessere C++ Möglichkeiten, diesen Teil zu tun, aber in meiner realen Anwendung wird mir ein char[] Puffer und eine Länge übergeben.

Dann schreibe ich den Puffer in ein std::stringstream-Objekt, aus dem ich jeweils eine Zeile entfernen kann.

Ich dachte, ich würde die Methode get( streambuf & ) auf dem Stringstream verwenden, um eine Zeile in einen anderen Stringstream zu schreiben, wo ich sie dann ausgeben kann.

Ungeachtet der Tatsache, dass dies vielleicht nicht der beste Weg ist, um eine Zeile nach der anderen aus dem Puffer zu extrahieren, den ich eingelesen habe (obwohl ich mich freuen würde, wenn jemand eine bessere Alternative als die hier gepostete vorschlagen könnte), sobald die erste ss.get( *s2.rdbuf() ) wird als die ss ist im Fehlerzustand und ich kann nicht herausfinden, warum. In der Eingabedatei sind viele Daten enthalten. ss sollte auf jeden Fall mehr als eine Eingabezeile enthalten.

Irgendwelche Ideen?

1voto

Jerry Coffin Punkte 452852

Es scheint mir, dass der erste (und wahrscheinlich größte) Schritt, um anständige Effizienz zu erhalten ist, um das Kopieren der Daten zu minimieren. Da Sie die Daten in einem char[] mit einer Länge gegeben werden, würde meine erste Tendenz sein, durch Erstellen einer strstream diesen Puffer zu verwenden. Dann statt Kopieren einer Zeichenfolge zu einem Zeitpunkt zu einem anderen strstream (oder stringstream) würde ich Zeichenfolgen eine zu einem Zeitpunkt in den Stream kopieren, die Sie verwenden, um sie auf die Ausgabe zu schreiben.

Wenn Sie den Inhalt des Puffers ändern dürfen, wäre eine weitere Möglichkeit, den Puffer in Zeilen zu zerlegen, indem Sie einfach jedes ' \n ' mit einem ' \0 '. Wenn Sie das tun, werden Sie normalerweise auch einen Vektor (deque, etc.) von Zeigern auf den Anfang jeder Zeile erstellen wollen (d.h. den ersten ' \r ' oder ' \n ', und ersetzen Sie es durch ein ' \0 '. Dann wird die nächste Sache außer einem ' \r ' oder ' \n ' ist der Anfang der nächsten Zeile, also ihre Adresse in Ihrem Vektor).

Ich würde mir auch gut überlegen, ob Sie die zeilenweise Ausgabe vermeiden können. Das Lesen durch einen großen Puffer, um Zeilenumbrüche zu finden, ist relativ langsam. Wenn Sie am Ende sowieso eine Zeile nach der anderen schreiben, könnten Sie all dies vermeiden, indem Sie einfach den gesamten Puffer in den Ausgabestrom schreiben und damit fertig sind.

0voto

Fredrik Jansson Punkte 3611

Ich habe dies unter Windows getestet, daher sollten Sie dies überprüfen;

Wenn data.out mit einem Zeilenumbruch beginnt, habe ich das gleiche Problem wie Sie, denn sonst funktioniert ss.get( *s2.rdbuf() ) beim ersten Aufruf problemlos.

Beim zweiten Aufruf ist die aktuelle Position des Streams noch nicht über den EOL hinausgekommen. Beim zweiten Aufruf versucht get also sofort, das EOL zu lesen, und da keine anderen Zeichen kopiert wurden, wird das Fail-Bit gesetzt.

Schnelle und vielleicht schmutzige Lösung:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X