std::stringstream如何设置fail/bad bit?

9

我经常使用的一个简单字符串分割代码如下:

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

一个stringstream就像是由string支持一样,所以我们不必担心读取文件时会出现的任何问题。这里没有进行任何类型转换,因为getline只是简单地读取直到看到行分隔符或EOF。所以我们不会遇到像boost::lexical_cast那样需要担心的任何错误。
我只能想到除分配的内存不足之外,其他什么都可能出错,但这只会在std::getline发生之前抛出一个std::bad_alloc。我错过了什么吗?

1
返回本地引用是错误的。 - UncleBens
1
不错的发现,虽然我并不是想返回一个本地引用,这只是一个简化的示例,以展示问题的基础知识。 - Evan Teran
1
只有在您没有调用“rdbuf(otherstreambuf)”时,“stringstream”才由“string”支持。 - Ben Voigt
1个回答

6
我无法想象这个人认为会发生什么错误,你应该要求他们解释。除了你提到的分配错误会被抛出而不是被忽略之外,没有任何问题。
我唯一看到你直接缺少的是,在 while 循环之后保证 ss.fail() 为 true,因为这是正在测试的条件。(bool(stream) 相当于 !stream.fail(),而不是 stream.good())预期的是,ss.eof() 也将为 true,表示失败是由于 EOF 导致的。
然而,可能会有一些混淆,关于实际发生了什么。因为 getline 使用以 delim 结尾的字段而不是以 delim 分隔的字段,输入数据例如 "a\nb\n" 有两个而不是三个字段,这可能令人惊讶。对于行来说,这完全有意义(并且是 POSIX 标准),但是如果使用 '-' 作为分隔符,在拆分 "a-b-" 时您期望找到多少个字段?

顺便说一下,这是我如何编写分割的:

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

这样可以避免iostream的所有问题,避免额外的复制(stringstream的后备字符串;并且substr返回的临时值,如果支持,甚至可以使用C++0x右值引用进行移动语义),具有我期望的split行为(不同于你的),并且适用于任何容器。
deque<string> c;
split("a-b-", '-', back_inserter(c));
// c == {"a", "b", ""}

使用s.fail()的好处是什么?我认为s.bad()可能是更好的选择,或者!s.eof()?(它应该由于EOF而结束,所以如果不是EOF,那么它就失败了,对吧?) - Evan Teran
另外,关于终止字段和分隔字段的区别,你提出了一个很好的观点。我以前从未遇到过这样的问题,但我可以想象它会让人感到惊讶。这更加需要在从结果中提取数据之前测试你得到的字段数量。 - Evan Teran
@Evan:首先确定您要检查的条件。在循环之后,无需检查ss上的失败、错误、eof或任何其他内容,但您可能需要检查elems,正如您所说的那样。 - Roger Pate
目前,该检查仅为假设性的。有人说这会吞掉一个错误,问题是,我该如何捕获这个错误?似乎这个错误检查根本不必要,我只需要检查我得到了预期的金额即可。 - Evan Teran
@Evan:它应该吞咽哪个错误?我找不到任何错误,并倾向于说他们完全错了。(除非他们指的是关于-terminated与-separated的区别,但这不是getline中的错误,而是一种误解。) - Roger Pate
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接