2010-04-01 15 views
9

Wspólny kawałek kodu używam do prostego podziału ciąg wygląda następująco:W jaki sposób std :: stringstream może ustawić bit 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; 
} 

Ktoś wspomniał, że będzie cicho „jaskółka” błędy występujące w std::getline. I oczywiście zgadzam się, że tak jest. Ale przyszło mi do głowy, co może pójść nie tak w praktyce, że będę musiał się martwić. w zasadzie to wszystko sprowadza się do tego:

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

stringstream jest wspierany przez string, więc nie musimy się martwić o jakichkolwiek problemów związanych z czytania z pliku. Od getline nie odbywa się tutaj żadna konwersja typu, dopóki nie zobaczy separatora linii lub EOF. Nie możemy więc uzyskać żadnego z błędów, o które musi się martwić coś w rodzaju boost::lexical_cast.

Po prostu nie mogę wymyślić niczego poza tym, że nie mogę przydzielić wystarczającej ilości pamięci, która mogłaby pójść źle, ale to po prostu rzuci std::bad_alloc na długo przed tym, zanim std::getline będzie miało miejsce. czego mi brakuje?

+1

Niepoprawne jest zwracanie odwołania do lokalnego. – UncleBens

+1

Dobry połów, chociaż nie chciałem zwrócić odniesienia do lokalnego, jest to przykładowy przykład demonstrujący podstawy pytania: –

+1

Ciąg 'stringstream' jest wspierany przez' string' tylko jeśli nie zadzwoniłeś 'rdbuf (otherstreambuf)'. –

Odpowiedz

6

Nie mogę sobie wyobrazić, jakie błędy może wydawać się ta osoba, i powinieneś poprosić o wyjaśnienie. Nic nie może pójść źle, z wyjątkiem błędów przydziału, jak wspomniałeś, które są rzucane i nie połykane.

Jedyne, co widzę, że bezpośrednio zaginąłeś to to, że ss.fail() ma pewność, że jest prawdziwe po pętli while, ponieważ to jest testowany warunek. (bool(stream) jest równoważne z !stream.fail(), niestream.good().) Zgodnie z oczekiwaniami, będzie również prawdziwe ss.eof(), wskazując, że awaria była spowodowana EOF.

Może się jednak zdarzyć zamieszanie związane z rzeczywistością. Ze względu getline wykorzystuje delim - zakończone pola zamiast delim - oddzielone pola, dane wejściowe, takie jak "a\nb\n" ma dwa zamiast trzech obszarów, a to może być zaskakujące. W przypadku linii ma to sens (i jest standardem POSIX), ale ile pól z delim z '-', czy spodziewałbyś się znaleźć w "a-b-" po podzieleniu?


Nawiasem mówiąc, oto jak ja bym 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; 
} 

Pozwala to uniknąć wszystkich problemów z iostreams w pierwszej kolejności, unika dodatkowych kopii (podkład String stringstream za; plus temp powrócił by substr może nawet użyć wartości rwartości C++ 0x dla semantyki ruchu, jeśli jest obsługiwana, jak napisano), ma zachowanie, którego oczekuję od podziału (różni się od twojego) i działa z każdym kontenerem.

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

dobry punkt na temat użycia 's.fail()', przypuszczam, że 's.bad()' byłby lepszym wyborem? lub może '! s.eof()'? (powinien się zakończyć z powodu EOF, więc jeśli nie jest to EOF, to nie udało się to?) –

+0

Również, dobry punkt o polach zakończonych a oddzielonych. Nigdy wcześniej nie miałem z tym problemu, ale widziałem, że to zaskakujące. Tym bardziej powód, aby przetestować liczbę pól przed wyodrębnieniem danych z wyniku. –

+0

@Evan: Najpierw sprawdź stan, który próbujesz sprawdzić. Nie ma potrzeby sprawdzania błędów *, * zł *, * eof, ani cokolwiek innego po pętli, ale możesz chcieć sprawdzić * elemy *, tak jak powiedziałeś. –

Powiązane problemy