std::stringstream 可以设置失败/坏位的方式?

Posted

技术标签:

【中文标题】std::stringstream 可以设置失败/坏位的方式?【英文标题】:Ways std::stringstream can set fail/bad bit? 【发布时间】: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 支持。 【参考方案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 中的错误,这是一个误解。)

以上是关于std::stringstream 可以设置失败/坏位的方式?的主要内容,如果未能解决你的问题,请参考以下文章

std::stringstream 错误?

为啥 std::stringstream 在静态内联时不能默认构造?

相当于 %02d 与 std::stringstream?

将 std::string 转换回使用 std::stringstream << cv::Mat 生成的 cv::Mat

与 libmx 链接并使用 std::stringstream 时出现 g++ malloc 错误

使用 stringstream 将字符串转换为 int