Пути std :: stringstream может установить сбой / плохой бит? - PullRequest
9 голосов
/ 01 апреля 2010

Обычный фрагмент кода, который я использую для простого разделения строк, выглядит следующим образом:

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

Кто-то упомянул, что это будет молча "проглатывать" ошибки, возникающие в std::getline. И, конечно, я согласен, что это так. Но мне пришло в голову, что здесь может пойти не так на практике , что мне нужно беспокоиться. в основном все сводится к этому:

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

A stringstream поддерживается string, поэтому нам не нужно беспокоиться о каких-либо проблемах, связанных с чтением из файла. Здесь нет преобразования типов, поскольку getline просто читает, пока не увидит разделитель строк или EOF. Таким образом, мы не можем получить ни одной ошибки, о которой должно беспокоиться что-то вроде boost::lexical_cast.

Я просто не могу придумать что-то кроме того, что не могу выделить достаточно памяти, которая может пойти не так, но это просто выбросит std::bad_alloc задолго до того, как std::getline даже произойдет. Чего мне не хватает?

1 Ответ

6 голосов
/ 02 апреля 2010

Я не могу представить, какие ошибки, по мнению этого человека, могут произойти, и вы должны попросить их объяснить. Как вы упомянули, ничто не может пойти не так, как ошибки выделения, которые выбрасываются и не проглатываются.

Единственное, что я вижу, что вы прямо упускаете, это то, что ss.fail() гарантированно будет истинным после цикла while, потому что это проверяемое условие. (bool(stream) эквивалентно !stream.fail(), , а не stream.good().) Как и ожидалось, ss.eof() также будет истинным, указывая на то, что сбой произошел из-за EOF.

Однако, может быть некоторая путаница в том, что на самом деле происходит. Поскольку getline использует delim - завершенные поля вместо delim - разделенные поля, входные данные, такие как "a\nb\n" имеет два поля вместо трех, и это может быть удивительно. Для строк это имеет смысл (и является стандартом POSIX), но сколько полей с delim , равным '-', вы ожидаете найти в "a-b-" после разбиения?


Кстати, вот как я напишу split :

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

Во-первых, это позволяет избежать всех проблем с iostreams, избежать лишних копий (строки поддержки stringstream; плюс temp, возвращаемая substr, может даже использовать ссылку на C ++ 0x rvalue для семантики перемещения, если поддерживается, как написано) , имеет поведение, которое я ожидаю от split (отличается от вашего), и работает с любым контейнером.

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