【问题标题】:Ways std::stringstream can set fail/bad bit?std::stringstream 可以设置失败/坏位的方式?
【发布时间】:2011-02-03 12:13:48
【问题描述】:

我用于简单字符串拆分的一段常见代码如下所示:

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

stringstreamstring 支持,因此我们不必担心与读取文件相关的任何问题。这里没有进行类型转换,因为getline 只是读取,直到它看到行分隔符或EOF。所以我们不会遇到像boost::lexical_cast 这样的错误需要担心的任何错误。

除了没有分配足够的内存可能会出错之外,我根本想不出什么,但这只会在std::getline 发生之前抛出一个std::bad_alloc。我错过了什么?

【问题讨论】:

  • 错误是返回对本地的引用。
  • 很好,虽然我并不是要返回对本地的引用,但这是一个简化的示例,用于展示问题的基础
  • 只有当您没有调用 rdbuf(otherstreambuf) 时,stringstream 才会由 string 支持。

标签: c++ stringstream getline


【解决方案1】:

我无法想象这个人认为可能会发生什么错误,您应该请他们解释。除了分配错误,没有什么可以出错的,正如你提到的,它们被抛出而不是被吞没。

我看到您直接缺少的唯一一件事是 ss.fail() 在 while 循环之后保证为真,因为这是正在测试的条件。 (bool(stream) 等同于 !stream.fail()not stream.good()。)正如预期的那样,ss.eof() 也将是 true,表明失败是由于 EOF。

但是,对于实际发生的情况可能会有些混淆。因为 getline 使用 delim-terminated 字段而不是 delim-分隔 字段,输入"a\nb\n" 这样的数据有两个而不是三个字段,这可能令人惊讶。对于行,这是完全有意义的(并且是 POSIX 标准),但是在拆分后,您希望在 "a-b-" 中找到多少个 delim'-' 的字段?


顺便说一下,我会这样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;
}

这首先避免了 iostreams 的所有问题,避免了额外的副本(stringstream 的支持字符串;加上 substr 返回的 temp 甚至可以使用 C++0x 右值引用作为移动语义,如果支持,如所写) ,具有我期望的 split 行为(与您的不同),并且适用于任何容器。

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

【讨论】:

  • 关于使用s.fail() 的好处,我想s.bad() 会是更好的选择吗?或者!s.eof()? (它应该由于EOF而结束,所以如果它不是EOF,那么它失败了对吧?)
  • 另外,关于终止字段与分离字段的要点。我以前从来没有遇到过这个问题,但我可以看到它令人惊讶。更有理由在从结果中提取数据之前测试您获得的字段数量。
  • @Evan:首先确定您要检查的条件。循环后无需检查 ss 上的 fail、bad、eof 或其他任何内容,但您可能需要检查 elems,如您所说。
  • 目前该检查是假设性的;)。有人说这会吞下一个错误,问题是,我将如何捕捉这个错误?似乎根本不需要错误检查,我只需要检查我是否得到了预期的金额。
  • @Evan:它应该吞下什么错误?我找不到,我倾向于说他们完全错了。 (除非他们的意思是 -terminated vs -separated,但这不是 getline 中的错误,这是一个误解。)
猜你喜欢
  • 1970-01-01
  • 2017-11-15
  • 1970-01-01
  • 1970-01-01
  • 2010-11-05
  • 2020-08-04
  • 1970-01-01
  • 1970-01-01
  • 2012-06-20
相关资源
最近更新 更多