Wie lese ich den gesamten Stream in einen std :: string?


76

Ich versuche, einen ganzen Stream (mehrere Zeilen) in eine Zeichenfolge einzulesen.

Ich benutze diesen Code und er funktioniert, aber er verletzt meinen Sinn für Stil ... Sicher gibt es einen einfacheren Weg? Vielleicht mit Stringstreams?

void Obj::loadFromStream(std::istream & stream)
{ 
  std::string s;

  std::streampos p = stream.tellg();  // remember where we are

  stream.seekg(0, std::ios_base::end); // go to the end
  std::streamoff sz = stream.tellg() - p;  // work out the size
  stream.seekg(p);        // restore the position

  s.resize(sz);          // resize the string
  stream.read(&s[0], sz);  // and finally, read in the data.


Eigentlich würde ein constVerweis auf eine Zeichenfolge auch genügen, und das könnte die Sache einfacher machen ...

const std::string &s(... a miracle occurs here...)

Antworten:


118

Wie wäre es mit

std::istreambuf_iterator<char> eos;
std::string s(std::istreambuf_iterator<char>(stream), eos);

(könnte ein Einzeiler sein, wenn nicht für MVP)

Nach der Bearbeitung nach 2011 wird dieser Ansatz jetzt geschrieben

std::string s(std::istreambuf_iterator<char>(stream), {});

2
Es könnte immer noch ein Einzeiler sein, wenn Sie wollen : string s = string(...).
Mike Seymour

1
Vielen Dank. Können Sie näher erläutern, was das tut? Muss eos nicht irgendwie initialisiert werden?
Roddy

13
@Roddy: Die Zeichenfolge besteht aus dem Bereich istreambuf_iterator, der über unformatierte Zeichen iteriert, bis er einem standardmäßig erstellten Eingabe-Iterator entspricht, der auch als "Ende des Streams" bezeichnet wird. Siehe Scott Meyers, Effektive STL Punkt 29: Betrachten Sie istreambuf_iterators für die zeichenweise Eingabe
Cubbi

1
Entnommen aus der Dokumentation zu std :: istream_iterator "Hinweise: Beim Lesen von Zeichen überspringt std :: istream_iterator standardmäßig Leerzeichen (sofern dies nicht mit std :: noskipws oder einem gleichwertigen Element deaktiviert ist), std :: istreambuf_iterator jedoch nicht. Außerdem std :: istreambuf_iterator ist effizienter, da der Aufwand für das einmalige Erstellen und Zerstören des Wachobjekts pro Zeichen vermieden wird. "
Paul Solt

Das Lesen von Byte für Byte ist aus dem Leistungs-POV nicht ganz effizient. Gibt es eine bessere Lösung, die größere Teile einliest? Vielleicht SSE nutzen?
Ajeh

27

Ich bin zu spät zur Party, aber hier ist eine ziemlich effiziente Lösung:

std::string gulp(std::istream &in)
{
    std::string ret;
    char buffer[4096];
    while (in.read(buffer, sizeof(buffer)))
        ret.append(buffer, sizeof(buffer));
    ret.append(buffer, in.gcount());
    return ret;
}

Ich habe ein Benchmarking durchgeführt und es stellt sich heraus, dass die std::istreambuf_iteratorTechnik ( die von der akzeptierten Antwort verwendet wird ) tatsächlich viel langsamer ist. Bei gcc 4.4.5 mit -O3ist es ungefähr ein 4,5-facher Unterschied auf meinem Computer, und die Lücke wird mit niedrigeren Optimierungseinstellungen größer.


5
In der Tat effizienter als meine Antwort, wie ein richtiges blockweises Lesen wäre. OP wollte jedoch den "einfachen" Weg, der oft das Gegenteil von "schnell" ist.
Cubbi

8
Die Verwendung von string :: Reserve (size_t) würde es noch effizienter machen.
Tim

Joey, optimiere mit -O2. Option -O3 ist nicht für den schnellsten, sondern für kompakten Code, wie ich mich erinnere.
Barney Szabolcs

2
@BarnabasSzabolcs: -Os steht für kompakten Code, -O3 für aggressive Optimierung und -O2 für weniger aggressive. Siehe gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
Joey Adams

In einigen Fällen müssen Sie mit verschiedenen Einstellungen experimentieren. Bestimmte Optimierungen verringern in bestimmten Fällen die Geschwindigkeit. (Mein Kommentar zu O2 / O2 ist aufgrund dieses Arguments ebenfalls falsch.) Siehe z. B. stackoverflow.com/questions/19470873/…
Barney Szabolcs

20

Du könntest es tun

std::string s;
std::ostringstream os;
os<<stream.rdbuf();
s=os.str();

aber ich weiß nicht, ob es effizienter ist.

Alternative Version:

std::string s;
std::ostringstream os;
stream>>os.rdbuf();
s=os.str();

1
Vielen Dank. Als Lösung finde ich das wirklich einfach und lesbar und benutze es. Ich akzeptierte jedoch Cubbis Antwort, da ich viel daraus gelernt hatte!
Roddy

Ja, dies ist die einzige Methode "Bis Eofbit lesen". Die anderen Methoden ( istream::get(streambuf*)und std::getline(istream, string)) lesen nur bis zu einem bestimmten Trennzeichen.
Tanz87

11

Sie können versuchen, etwas von Algorithmen zu verwenden. Ich muss mich auf die Arbeit vorbereiten, aber hier ist ein sehr kurzer Überblick über die Dinge (es muss einen besseren Weg geben):

copy( istreambuf_iterator<char>(stream), istreambuf_iterator<char>(), back_inserter(s) );

0

Nun, wenn Sie nach einer einfachen und "lesbaren" Möglichkeit suchen, dies zu tun. Ich würde empfehlen, ein hochrangiges Framework für Ihr Projekt hinzuzufügen / zu verwenden. Dafür benutze ich immer Poco und Boost für alle meine Projekte. In diesem Fall mit Poco:

    string text;
    FileStream fstream(TEXT_FILE_PATH);
    StreamCopier::copyToString(fstream, text);

0

Was ist mit getline mit Trennzeichen? Der nächste Code hilft mir, das ganze std :: cin auf Ubuntu mit g ++ - 10 in einen String zu lesen.

#include <iostream>
#include <string>

using namespace std;

int main() {
    string s;

    getline(cin, s, {}); //the whole stream into variable s

    return 0;
}

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.