2010-04-01 24 views
9

Un morceau commun de code que j'utilise pour séparer de chaîne simple ressemble à ceci:Comment std :: stringstream peut-il définir un échec/un mauvais bit?

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

Quelqu'un a mentionné que cette erreur silencieusement « swallow » se produisant dans std::getline. Et bien sûr, je suis d'accord que c'est le cas. Mais il me vint à l'esprit, ce qui pourrait mal tourner ici dans la pratique que je devrais avoir à s'inquiéter. essentiellement tout se résume à ceci:

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 est soutenu par une string, donc nous n'avons pas à vous soucier des problèmes liés à la lecture d'un fichier. Il n'y a aucune conversion de type en cours ici puisque getline lit simplement jusqu'à ce qu'il voit le délimiteur de ligne ou EOF. Donc, nous ne pouvons pas avoir des erreurs que quelque chose comme boost::lexical_cast doit s'inquiéter.

Je ne peux tout simplement pas penser à quelque chose en plus de ne pas allouer assez de mémoire qui pourrait mal tourner, mais cela va juste lancer un std::bad_alloc bien avant que le std::getline ait lieu. Qu'est-ce que je rate?

+1

Ce qui ne va pas, c'est de renvoyer une référence à un local. – UncleBens

+1

Bonne capture, bien que je ne voulais pas renvoyer une référence à un local, ceci est un exemple réduit pour démontrer les bases de la question –

+1

Un 'stringstream' est soutenu par une' chaîne' que si vous n'avez pas appelé 'rdbuf (otherstreambuf)'. –

Répondre

6

Je ne peux pas imaginer quelles erreurs cette personne pense pourrait se produire, et vous devriez leur demander d'expliquer. Rien ne peut aller mal sauf les erreurs d'allocation, comme vous l'avez mentionné, qui sont jetées et non avalées. La seule chose que je vois que vous manquez directement est que ss.fail() est garanti être vrai après la boucle while, parce que c'est la condition testée. (bool(stream) est équivalent à !stream.fail(), et nonstream.good().) Comme prévu, ss.eof() sera également vrai, indiquant que l'échec était dû à EOF.

Cependant, il pourrait y avoir une certaine confusion sur ce qui se passe réellement. Parce que getline utilise delim - mis fin à champs plutôt que delim - séparés champs, les données d'entrée telles que "a\nb\n" a deux au lieu de trois champs, et cela pourrait être surprenant. Pour les lignes, cela prend tout son sens (et est POSIX standard), mais combien de champs, avec delim de '-', attendez-vous à trouver dans "a-b-" après la division?


Soit dit en passant, voici comment je 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; 
} 

Cela évite tous les problèmes avec iostreams en premier lieu, évite des copies supplémentaires (la chaîne de soutien du stringstream, plus la température est revenu par substr peut même utiliser une référence C++ 0x rvalue pour la sémantique de déplacement si elle est supportée, comme écrit), a le comportement que j'attends de split (différent du vôtre), et fonctionne avec n'importe quel conteneur.

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

bon point sur l'utilisation 's.fail()', je suppose que 's.bad()' serait un meilleur choix? ou peut-être '! s.eof()'? (Il devrait se terminer en raison de EOF, donc si ce n'est pas EOF, alors il a échoué à droite?) –

+0

Aussi, bon point sur les champs terminés vs séparés. Je n'ai jamais eu de problème avec ça auparavant, mais je pouvais voir que c'était surprenant. Raison de plus pour tester le nombre de champs que vous avez avant d'extraire vos données du résultat. –

+0

@Evan: Identifiez d'abord la condition que vous essayez de vérifier. Il n'y a pas besoin de vérifier fail, bad, eof, ou quoi que ce soit d'autre sur * ss * après la boucle, mais vous pourriez vouloir vérifier * elems *, comme vous l'avez dit. –