2010-04-01 4 views
9

Un comune pezzo di codice che uso per semplice scissione stringa assomiglia a questo:Modi std :: stringstream può impostare il bit di fail/bad?

inline std::vector<std::string> split(const std::string &s, char delim) { 
    std::vector<std::string> elems; 
    std::stringstream ss(s); 
    std::string item; 
    while(std::getline(ss, item, delim)) { 
     elems.push_back(item); 
    } 
    return elems; 
} 

Qualcuno ha detto che questo sarà silenziosamente "rondine" errori che si verificano in std::getline. E ovviamente sono d'accordo che sia così. Ma mi è venuto in mente che cosa potrebbe andare storto qui nella pratica di cui dovrei preoccuparmi. in fondo tutto si riduce a questo:

inline std::vector<std::string> split(const std::string &s, char delim) { 
    std::vector<std::string> elems; 
    std::stringstream ss(s); 
    std::string item; 
    while(std::getline(ss, item, delim)) { 
     elems.push_back(item); 
    } 

    if(/* what error can I catch here? */) { 
     // *** How did we get here!? *** 
    } 

    return elems; 
} 

Un stringstream è sostenuta da una string, in modo che non devono preoccuparsi di nessuno dei problemi connessi con la lettura da un file. Non è in corso alcuna conversione di tipo in quanto dal getline si legge semplicemente finché non viene visualizzato il delimitatore di riga o EOF. Quindi non possiamo ottenere nessuno degli errori che qualcosa come boost::lexical_cast deve preoccupare.

Semplicemente non riesco a pensare a qualcosa oltre a non riuscire ad allocare abbastanza memoria che potrebbe andare storta, ma questo semplicemente getterà uno std::bad_alloc molto prima che lo std::getline abbia luogo anche. Cosa mi manca?

+1

Ciò che è sbagliato è restituire un riferimento a un locale. – UncleBens

+1

Buona cattura, anche se non intendevo restituire un riferimento a un locale, questo è un esempio ridotto per dimostrare le basi della domanda –

+1

Un 'stringstream' è supportato da una' stringa' solo se non hai chiamato 'rdbuf (otherstreambuf)'. –

risposta

6

Non riesco a immaginare quali errori possa pensare questa persona e dovresti chiedere loro di spiegare. Niente può andare storto tranne gli errori di allocazione, come hai detto, che vengono lanciati e non ingoiati.

L'unica cosa che vedo che ti manca direttamente è che ss.fail() è garantito essere true dopo il ciclo while, perché è la condizione in fase di test. (bool(stream) è equivalente a !stream.fail(), nonstream.good().) Come previsto, anche ss.eof() sarà vero, a indicare che l'errore era dovuto a EOF.

Tuttavia, potrebbe esserci una certa confusione su ciò che sta realmente accadendo. Poiché getline utilizza delim - terminato campi anziché delim - separato campi dati di input quali "a\nb\n" ha due invece di tre campi, e questo potrebbe essere sorprendente. Per le linee questo ha senso (ed è standard POSIX), ma quanti campi, con un delim di '-', ti aspetteresti di trovare in "a-b-" dopo la divisione?


Per inciso, ecco come farei writesplit:

template<class OutIter> 
OutIter split(std::string const& s, char delim, OutIter dest) { 
    std::string::size_type begin = 0, end; 
    while ((end = s.find(delim, begin)) != s.npos) { 
    *dest++ = s.substr(begin, end - begin); 
    begin = end + 1; 
    } 
    *dest++ = s.substr(begin); 
    return dest; 
} 

Questo evita tutti i problemi con iostreams, in primo luogo, evita copie extra (stringa di sostegno del stringstream, in più la temperatura è tornato da substr può anche usare un riferimento di runtime C++ 0x per semantica di movimento se supportato, come scritto), ha il comportamento che mi aspetto da split (diverso dal tuo) e funziona con qualsiasi contenitore.

deque<string> c; 
split("a-b-", '-', back_inserter(c)); 
// c == {"a", "b", ""} 
+0

buon punto sull'uso 's.fail()', suppongo che 's.bad()' sarebbe una scelta migliore? o forse '! s.eof()'? (dovrebbe finire a causa di EOF, quindi se non è EOF, allora ha fallito proprio?) –

+0

Inoltre, buon punto sui campi terminati vs separati. Non ho mai avuto un problema con quello prima, ma ho potuto vedere che è sorprendente. Un motivo in più per testare il numero di campi che hai ottenuto prima di estrarre i tuoi dati dal risultato. –

+0

@ Evan: per prima cosa identifica la condizione che stai cercando di verificare. Non è necessario controllare fail, bad, eof o qualsiasi altra cosa su * ss * dopo il ciclo, ma potresti voler controllare * elems *, come hai detto tu. –