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

Astringstream由 a 支持string,因此我们不必担心与从文件读取相关的任何问题。这里没有进行类型转换,因为getline只是读取直到看到行分隔符或EOF. 所以我们不能得到任何boost::lexical_cast需要担心的错误。

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

4

1 回答 1

6

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

我看到您直接缺少的唯一一件事是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 的支持字符串;加上 substr 返回的 temp 如果支持,甚至可以使用 C++0x 右值引用进行移动语义,如所写),具有我期望 split 的行为(与你的不同),并且适用于任何容器。

deque<string> c;
split("a-b-", '-', back_inserter(c));
// c == {"a", "b", ""}
于 2010-04-01T20:43:40.830 回答