2010-07-21 17 views
18

Ho bisogno di copiare un file in una stringa. Ho bisogno in qualche modo di preallocare la memoria per quell'oggetto stringa e un modo per leggere direttamente il contenuto del file nella memoria di quella stringa?come pre-allocare memoria per un oggetto std :: string?

+4

possibile duplicato di [Read intero file ASCII in C++ std :: string] (http://stackoverflow.com/questions/2602013/read-whole-ascii-file-into-c-stdstring) - La mia risposta accettata a questa domanda spiega anche come pre-allocare tutta la memoria in modo che la stringa non si espanda ripetutamente durante la lettura. –

risposta

6

Questo dovrebbe essere tutto ciò che serve:

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

string s = os.str(); 

Questa legge i caratteri da file e li inserisce nel stringstream. Successivamente ottiene la stringa creata dietro le quinte. Si noti che sono caduto nella seguente trappola: l'utilizzo dell'operatore di estrazione salterà lo spazio vuoto iniziale. Devi usare l'operatore di inserimento come sopra, o utilizzare il noskipws manipolatore:

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

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

Queste funzioni sono descritte come leggere il carattere flusso per carattere anche se (non so cosa le ottimizzazioni sono possibili qui, però), Mi rifugio' t cronometrati per determinare la loro velocità.

+1

+1 mi picchia. :( – GManNickG

+1

questo copia due volte, una volta al buffer 'ostringstream' e la seconda volta a' s' –

+0

@Johannes, stavo assumendo che la memoria delle stringhe fosse un buffer contagioso, ma dopo aver letto la risposta di @GMan mi sono reso conto che non c'è –

5

Solo per divertimento, ecco un altro modo per farlo:

// 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>()); 

spero non mi soffiare troppo male.

+2

+1, aspettavo che qualcuno elaborasse il codice basato su iteratore in streaming :) – bobah

+0

+1, per 'Brain-Compilatore flessibile 'è ok con mancante'} ';) –

+0

@Gollum: Ammetto che ho copiato queste righe 'seekg()' direttamente da un mio codice (che riempie una stringa con il contenuto di un file) e ho trascurato il '{'. L'ho risolto, ma è per quello che è il disclaimer. – sbi

14

Questa non è tanto una risposta in sé, come una sorta di commento/riassunto/confronto di un paio di altre risposte (oltre a una rapida dimostrazione del perché ho raccomandato lo stile del codice @ Johannes - litb cede nella sua risposta). Dal momento che @sbi ha pubblicato un'alternativa che sembrava abbastanza buona, e (soprattutto) ha evitato la copia extra coinvolta nella lettura in uno stringstream, quindi usando il membro .str() per ottenere una stringa, ho deciso di scrivere un rapido confronto tra i due:

[Modifica: ho aggiunto un terzo test case usando il codice basato su @Tyler McHenry istreambuf_iterator e aggiunto una riga per stampare la lunghezza di ogni stringa che è stata letta per garantire che l'ottimizzatore non ottimizzasse la lettura perché il risultato non è mai stato utilizzato]

[Edit2:. E ora, è stato aggiunto il codice da Martin York, nonché ...]

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

Ora la parte impressionante - i risultati. Prima con VC++ (nel caso qualcuno se ne frega, il codice di Martin è abbastanza veloce ho aumentato la dimensione del file per ottenere un tempo significativo per esso):

s.length() = 7.669.436
s2.length = 6.390.688
s3.length = 7669436
s4.lunghezza = 7669436
Ora utilizzando rdbuf: 184
Ora utilizzando istream_iterator: 1332
Ora utilizzando istreambuf_iterator: 249
Ora utilizzando leggere: 48

Poi con gcc (cygwin):

s.length() = 8278035
s2.length = 6390689
s3.length = 8278035
s4.length = 8.278.035
Tempo usando rdbuf: 62
Tempo usando istream_iterator: 2199
Tempo usando istreambuf_iterator: 156
tempo con leggere: 16

[fine della modifica - le conclusioni rimangono, anche se il vincitore è cambiato - il codice di Martin è chiaramente il più veloce è. ]

I risultati sono abbastanza coerenti rispetto ai quali è il più veloce e il più lento. L'unica incoerenza riguarda il modo in cui molto più veloce o più lento di uno. Sebbene i posizionamenti siano gli stessi, le differenze di velocità sono molto più grandi di con gcc che con VC++.

+0

Ordinamento di ciò che pensavo inizialmente: è molto più semplice ottimizzare la lettura char-by-char di op << in una lettura blocco (o inline parti appropriate) rispetto alla lettura char-by-char di istream_iterator (sebbene tale codice debba usa 'istreambuf_iterator' per evitare di saltare lo spazio bianco per ogni carattere letto - forse questo accelera le cose da quando sta accadendo a un livello inferiore?), che va su più passaggi con op ++, op * ecc. Ma non mi aspettavo che avrebbe reso * quella * molta differenza. Grazie per il tempismo! –

+1

Potresti scrivere quali flag di compilazione sono stati utilizzati per i casi di test? – tomekpe

1

Sembra che tu stia chiedendo come eseguire un'operazione di tipo CString :: GetBuffer, ReleaseBuffer con std :: string.

Non conosco alcun modo per farlo direttamente, un modo semplice sarebbe semplicemente creare un buffer di stile C grezzo, leggere nel buffer, quindi copiare il buffer in una stringa: std :: assign o qualunque . Ovviamente dovresti preoccuparti dei problemi di sovraccarico del buffer, ecc. Vorrei usare anche uno std :: autoptr per gestire il puntatore del buffer raw, enusre deallocation su exception ecc. Questo è un po 'più semplice dell'uso di stringstream ecc. Posso fornire un esempio se necessario.

Devin Ellingson

+1

'auto_ptr' non gestisce correttamente i tipi di array. Basta usare 'std :: vector'. (Inoltre, la firma dei post è disapprovata, il tuo nome è sotto tutto quello che fai.) – GManNickG

+0

Grazie a GMan, ho dimenticato questo problema, auto_ptr chiama sempre delete anziché delete [], che è ciò che sarebbe necessario in questo caso. Si potrebbe semplicemente creare una semplice classe array_auto_ptr o come dici usare std :: vector. – DevinEllingson

Problemi correlati