2010-07-21 13 views
18

Ich muss eine Datei in eine Zeichenfolge kopieren. Ich brauche einen Weg, um Speicher für dieses String-Objekt vorzuladen und den Dateiinhalt direkt in den Speicher dieser Zeichenfolge zu lesen.wie Speicherplatz für ein Std :: String-Objekt vorab reservieren?

+4

möglich Duplikat [Lesen ganze ASCII-Datei in C++ std :: string] (http://stackoverflow.com/questions/2602013/read-whole-ascii-file-into-c-stdstring) - Meine akzeptierte Antwort auf diese Frage erklärt auch, wie der gesamte Speicher vorbelegt wird, so dass sich der String während des Lesens nicht wiederholt ausdehnt. –

Antwort

6

Dies sollte alles, was Sie brauchen werden:

ostringstream os; 
ifstream file("name.txt"); 
os << file.rdbuf(); 

string s = os.str(); 

Dieses liest Zeichen aus file und fügt sie in die string. Danach wird die hinter den Kulissen erstellte Zeichenfolge abgerufen. Beachten Sie, dass ich in die folgende Falle geriet: Die Verwendung des Extraktionsoperators überspringt den anfänglichen Leerraum. Sie haben die Ausgabe-Operator wie oben zu verwenden, oder verwenden Sie die noskipws Manipulator:

// Beware, skips initial whitespace! 
file >> os.rdbuf(); 

// This does not skip it 
file >> noskipws >> os.rdbuf(); 

Diese Funktionen wie das Lesen der Strom zeichenweise obwohl beschrieben werden (nicht sicher, welche Optimierungen möglich sind hier, obwohl), i haven‘ t Zeit diese um ihre Geschwindigkeit zu bestimmen.

+1

+1 schlägt mich. :( – GManNickG

+1

dies kopiert zweimal, einmal zu 'ostringstream' Puffer und zweites Mal zu' s' –

+0

@Johannes, nahm ich an, dass der Schnurspeicher ansteckender Puffer ist, aber nach dem Lesen von @GMan 's Antwort realisierte ich, dass es nein gibt Um die Kopie herum: –

5

Just for fun, hier ist eine andere Art und Weise, dies zu tun:

// Beware, brain-compiled code ahead! 

std::ifstream ifs(/* ... */); 
if(!ifs.good()) return; // whatever 

std::string str; 

ifs.seekg(0, std::ios_base::end); 
const std::streampos pos = ifs.tellg(); 
ifs.seekg(0, std::ios_base::beg); 
if(pos!=std::streampos(-1)) // can get stream size? 
    str.reserve(static_cast<std::string::size_type>(pos)); 

str.assign(std::istream_iterator<char>(ifs) 
      , std::istream_iterator<char>()); 

ich, dass ich es so schlecht nicht hoffen blasen.

+2

+1, wartete auf jemanden, um Stream Iterator-basierte Code zu erarbeiten :) – bobah

+0

+1, für flexible 'Brain-Compiler' ist in Ordnung mit fehlenden'} ';) –

+0

@Gollum: Ich gebe frei zu, dass ich diese 'seekg()' Zeilen direkt aus irgendeinem Code von mir kopiert habe (der einen String mit dem Inhalt einer Datei füllt) und das '{'. Ich habe es behoben, aber dafür ist der Disclaimer sowieso gedacht. – sbi

14

Dies ist nicht so eine Antwort in sich selbst, als eine Art Kommentar zu/Zusammenfassung/Vergleich von ein paar anderen Antworten (sowie eine kurze Demonstration, warum ich den Stil des Codes @ empfohlen habe @ Johannes - litb gibt in seiner Antwort). Da @sbi eine Alternative gepostet hat, die ziemlich gut aussah, und (besonders) die zusätzliche Kopie beim Einlesen in einen Stringstream vermied und dann das .str() Mitglied benutzte, um eine Zeichenfolge zu erhalten, entschied ich mich, einen schnellen Vergleich der beiden zu schreiben:

[Bearbeiten: Ich habe einen dritten Testfall mit @Tyler McHenry istreambuf_iterator-basierten Code hinzugefügt und eine Zeile hinzugefügt, um die Länge der einzelnen gelesenen Zeichenfolge auszudrucken, um sicherzustellen, dass der Optimierer nicht die Lesung optimiert weil das Ergebnis wurde nie verwendet]

[Edit2:. Und jetzt, Code von Martin Yorker wurde auch ...]

#include <fstream> 
#include <sstream> 
#include <string> 
#include <iostream> 
#include <iterator> 
#include <time.h> 

int main() { 
    std::ostringstream os; 
    std::ifstream file("equivs2.txt"); 

    clock_t start1 = clock(); 
    os << file.rdbuf(); 
    std::string s = os.str(); 
    clock_t stop1 = clock(); 

    std::cout << "\ns.length() = " << s.length(); 

    std::string s2; 

    clock_t start2 = clock(); 
    file.seekg(0, std::ios_base::end); 
    const std::streampos pos = file.tellg(); 
    file.seekg(0, std::ios_base::beg); 

    if(pos!=std::streampos(-1)) 
     s2.reserve(static_cast<std::string::size_type>(pos)); 
    s2.assign(std::istream_iterator<char>(file), std::istream_iterator<char>()); 
    clock_t stop2 = clock(); 

    std::cout << "\ns2.length = " << s2.length(); 

    file.clear(); 

    std::string s3; 

    clock_t start3 = clock(); 
    file.seekg(0, std::ios::end); 
    s3.reserve(file.tellg()); 
    file.seekg(0, std::ios::beg); 

    s3.assign((std::istreambuf_iterator<char>(file)), 
      std::istreambuf_iterator<char>()); 
    clock_t stop3 = clock(); 

    std::cout << "\ns3.length = " << s3.length(); 

    // New Test 
    std::string s4; 

    clock_t start4 = clock(); 
    file.seekg(0, std::ios::end); 
    s4.resize(file.tellg()); 
    file.seekg(0, std::ios::beg); 

    file.read(&s4[0], s4.length()); 
    clock_t stop4 = clock(); 

    std::cout << "\ns4.length = " << s3.length(); 

    std::cout << "\nTime using rdbuf: " << stop1 - start1; 
    std::cout << "\nTime using istream_iterator: " << stop2- start2; 
    std::cout << "\nTime using istreambuf_iterator: " << stop3 - start3; 
    std::cout << "\nTime using read: " << stop4 - start4; 
    return 0; 
} 
hinzugefügt

Jetzt der beeindruckende Teil - die Ergebnisse. Zuerst mit VC++ (falls jemand kümmert, Martin Code ist schnell genug, ich die Dateigröße eine sinnvolle Zeit für sie zu bekommen erhöht):

s.length() = 7.669.436
s2.length = 6.390.688
s3.length = 7669436
s4.length = 7669436
Zeit unter Verwendung von RDBUF: 184
Zeit unter Verwendung von istream_iterator: 1332
Zeit unter Verwendung von istreambuf_iterator: 249
Zeit gelesen werden: 48

dann mit gcc (cygwin):

s.length() = 8278035
s2.length = 6390689
s3.length = 8278035
s4.length = 8.278.035
Zeit mit RDBUF: 62
Zeit mit istream_iterator: 2199
Zeit mit istreambuf_iterator: 156
Zeit ausgelesen: 16

[Ende bearbeiten - die Schlussfolgerungen bleiben, obwohl der Gewinner sich geändert hat - Martins Code ist klar der schnellste. ]

Die Ergebnisse sind ziemlich konsistent in Bezug auf die schnellste und langsamste. Die einzige Inkonsistenz ist, wie viel schneller oder langsamer ist als ein anderer. Obwohl die Platzierungen die gleichen sind, sind die Geschwindigkeitsunterschiede viel größer mit gcc als mit VC++.

+0

Eine Art von dem, was ich anfangs dachte: Es ist viel einfacher, das char-by-char-Lesen von op << in einen Block zu lesen (oder inline entsprechende Teile) als das char-by-char-Lesen von istream_iterator (obwohl dieser Code muss) Verwenden Sie 'istreambuf_iterator', um das Überspringen von Leerraum für jedes gelesene Zeichen zu vermeiden - vielleicht wird das die Dinge beschleunigen, da es auf einer niedrigeren Ebene stattfindet?), die mehrere Schritte mit op ++, op * usw. durchläuft * Das * ein großer Unterschied. Danke für das Timing! –

+1

Können Sie schreiben, welche Kompilierungsflags für Testfälle verwendet wurden? – tomekpe

1

Es scheint, dass Sie fragen, wie eine Operation CString :: GetBuffer, ReleaseBuffer Typ mit Std :: string.

Ich kenne keine Möglichkeit, dies direkt zu tun, eine einfache Möglichkeit wäre, nur einen rohen C-Style-Puffer zu erstellen, in den Puffer zu lesen und dann den Puffer mit assign oder was auch immer in eine std :: string zu kopieren . Natürlich müssten Sie sich über Pufferüberlaufprobleme usw. Sorgen machen, außerdem würde ich einen std :: autoptr verwenden, um den unformatierten Pufferzeiger zu verwalten, um die Freigabe für eine Ausnahme freizugeben usw. Dies ist etwas einfacher als die Verwendung von Stringstream usw. ein Beispiel, falls benötigt.

Devin Ellingson

+1

'auto_ptr' behandelt Array-Typen nicht korrekt. Verwenden Sie einfach 'std :: vector'. (Auch das Signieren von Posts ist verpönt; Ihr Name steht unter allem, was Sie tun.) – GManNickG

+0

Danke GMan, ich habe dieses Problem vergessen, auto_ptr ruft immer delete statt delete [] auf, was in diesem Fall nötig wäre. Sie könnten einfach eine einfache array_auto_ptr Klasse erstellen oder wie Sie sagen, std :: vector verwenden. – DevinEllingson

Verwandte Themen