2009-05-18 22 views
28

Apparentemente boost::asio::async_read non ama le stringhe, poiché l'unico sovraccarico di boost::asio::buffer mi consente di creare const_buffer s, quindi sono bloccato a leggere tutto in uno streambuf.
Ora voglio copiare il contenuto dello streambuf in una stringa, ma a quanto pare supporta solo la scrittura in char * (sgetn()), creando un istream con lo streambuf e utilizzando getline().Copia i contenuti di uno streambuf in una stringa

C'è un altro modo per creare una stringa con il contenuto dello streambufs senza una copia eccessiva?

risposta

42

Non so se si conta come "eccessiva copia", ma è possibile utilizzare uno stringstream:

std::ostringstream ss; 
ss << someStreamBuf; 
std::string s = ss.str(); 

Come, di leggere tutto da stdin in una stringa, fare

std::ostringstream ss; 
ss << std::cin.rdbuf(); 
std::string s = ss.str(); 

In alternativa, è possibile utilizzare anche un istreambuf_iterator. Dovrai misurare se questa o quella via è più veloce - non lo so.

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

noti che someStreamBuf sopra vuole rappresentare un streambuf*, quindi prendere il suo indirizzo appropriato. Notare anche le parentesi aggiuntive attorno al primo argomento nell'ultimo esempio, in modo che non lo interpreti come una dichiarazione di funzione che restituisce una stringa e prende un iteratore e un altro puntatore a funzione ("parse più irritante").

+2

Grazie, istreambuf_iterator era quello che stavo cercando. – tstenner

+1

Ecco qualcosa di strano. Non riesco a capire perché, ma istreambuf_iterator taglia gli ultimi simboli (se sono più di 20 simboli nell'ultima riga). C'è qualche idea del perché potrebbe essere? –

+0

ss << someStreamBuf copia gli 11 byte '0xFFFFFFFFF' che rappresentano l'indirizzo dello streambuf in memoria. Grazie. molto utile: D –

-1

credo che sia più simile a:


streambuf.commit(number_of_bytes_read); 

istream istr(&streambuf); 
string s; 
istr >> s; 

Non ho guardato nel codice basic_streambuf, ma credo che dovrebbe essere solo una copia nella stringa.

+4

operatore >> (istream &, string &) copia solo fino al primo spazio vuoto – tstenner

+0

Oh, ci siamo dimenticati :) –

+0

@tstenner C'è un flag per disabilitare quel comportamento, anche come altre bandiere aggiungono comportamenti diversi. – Etherealone

23

Un'altra possibilità con boost::asio::streambuf è quello di utilizzare in combinazione con boost::asio::buffer_cast<const char*>()boost::asio::streambuf::data() e boost::asio::streambuf::consume() come questo:

const char* header=boost::asio::buffer_cast<const char*>(readbuffer.data()); 
//Do stuff with header, maybe construct a std::string with std::string(header,header+length) 
readbuffer.consume(length); 

Questo non funzionerà con streambuf normale e potrebbe essere considerato sporco, ma sembra essere il modo più veloce di farlo.

+0

da dove viene la lunghezza? –

+0

È il numero di byte che hai preso dallo streaming. Vedi http://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/reference/basic_streambuf/consume.html – tstenner

+0

Cosa intendi con "streambufs normali"? La risposta di Sean DeNigris muggito in base alla documentazione non sarebbe valida? – ChrisPeterson

0

Il motivo per cui puoi creare const_buffer solo da std :: string è perché std :: string esplicitamente non supporta la scrittura diretta basata sul puntatore nel suo contratto. Potresti fare qualcosa di malvagio come ridimensionare la tua stringa ad una certa dimensione, quindi const_cast la costanza da c_str() e trattarla come un buffer raw char *, ma è molto cattivo e ti metterà nei guai un giorno.

Io uso std :: vector per i miei buffer perché fino a quando il vettore non viene ridimensionato (o si sta attenti a occuparsi del ridimensionamento), è possibile eseguire direttamente la scrittura del puntatore. Se ho bisogno di alcuni dati come std :: string, devo copiarlo, ma il modo in cui gestisco i miei buffer di lettura, tutto ciò che deve durare oltre il callback letto deve essere copiato a prescindere.

12

Per boost::asio::streambuf è possibile trovare una soluzione come questa:

boost::asio::streambuf buf; 
    /*put data into buf*/ 

    std::istream is(&buf); 
    std::string line; 
    std::getline(is, line); 

Stampa la stringa:

std::cout << line << std::endl; 

Si possono trovare qui: http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/async_read_until/overload3.html

+0

Questa risposta è decisamente migliore se si utilizza 'async_read_until' perché quella chiamata può restituire più di una riga di dati e questa risposta lascia correttamente i dati extra nello' streambuf'. –

36

E 'davvero sepolto in the docs. ..

Dato boost::asio::streambuf b, con size_t buf_size ...

boost::asio::streambuf::const_buffers_type bufs = b.data(); 
std::string str(boost::asio::buffers_begin(bufs), 
       boost::asio::buffers_begin(bufs) + buf_size); 
+3

Funziona bene! Puoi usare 'b.size()' invece di 'buf_size' se vuoi che l'intera cosa sia messa nella stringa. – Malvineous

+1

Si suppone che la risposta di tstenner sopra dove dice che è considerata sporca e non funzionerà con 'streambufs normali' si applica a questa risposta o in questo modo è considerata sicura? – ChrisPeterson

1

si può anche ottenere i personaggi asio::streambuf utilizzando std::basic_streambuf::sgetn:

asio::streambuf in; 
// ... 
char cbuf[in.size()+1]; int rc = in.sgetn (cbuf, sizeof cbuf); cbuf[rc] = 0; 
std::string str (cbuf, rc); 
-1

ho provato la prima risposta e ottenuto un errore di compilazione quando si compila con "g ++ -STD = C++ 11" Quello che ha funzionato per me è stato:

 #include <string> 
     #include <boost/asio.hpp> 
     #include <sstream>   
     //other code ... 
     boost::asio::streambuf response; 
     //more code 
     std::ostringstream sline; 
     sline << &response; //need '&' or you a compiler error 
     std::string line = sline.str(); 

Questo è stato compilato e eseguito.

+0

haha ​​questo restituisce il puntatore alla risposta. – thang

0

Una risposta più semplice sarebbe quello di convertire in std::string e manipolarlo qualche cosa come questo

std::string buffer_to_string(const boost::asio::streambuf &buffer) 
{ 
    using boost::asio::buffers_begin; 
    auto bufs = buffer.data(); 
    std::string result(buffers_begin(bufs), buffers_begin(bufs) + buffer.size()); 
return result; 
} 

Dare un codice molto conciso per l'attività.

+0

Questa è la stessa risposta di Sean DeNigris ... che ha pubblicato 4 anni prima! –