【问题标题】:Splitting a string拆分字符串
【发布时间】:2010-10-28 21:36:22
【问题描述】:

我有这个代码来分割一个字符串。出于某种原因,它只是坐在那里无所事事。我不确定问题是什么。顺便说一下,delim = ' ' 在这里。

vector<string> split( const string &str, const char &delim )
{
    typedef string::const_iterator iter;

    iter beg = str.begin();

    vector<string> tokens;

    while(beg != str.end())
    {
        iter temp = find(beg, str.end(), delim);
        if(beg != str.end())
            tokens.push_back(string(beg, temp));
        beg = temp;
    }

    return tokens;
}

【问题讨论】:

  • 顺便说一句,在 while 循环中测试“beg != str.end()”是没有意义的。自从测试条件的循环顶部以来,您没有更改 beg 或 str 。此外,通过值而不是 const 引用传递 char 更为常见。这些都不是错误,它们只是让您的代码更简单、更易于阅读,而不会影响行为。

标签: c++


【解决方案1】:

find() 将返回下一个标记的位置 X。然后,当您将其分配给 beg 并进入下一次迭代时,它将开始在位置 X 再次搜索 - 一次又一次 - 一次又一次......即您陷入了无限循环。

试试这个代码:

vector<string> split( const string &str, const char &delim )
{
    typedef string::const_iterator iter;

    vector<string> tokens;
    iter pos = str.begin(), last = str.begin();

    while(pos != str.end()) {
        last = pos;
        pos = find(pos, str.end(), delim);

        if (pos != str.end()) {
            string token = string(last, pos);
            if (token.length() > 0)
                tokens.push_back(token);

            last = ++pos;
        }
    }

    string lastToken = string(last, pos);
    if (lastToken.length() > 0)
        tokens.push_back(lastToken);

    return tokens;
}

这有一个额外的好处,它将包括列表中的最后一个标记(例如,当在空间上拆分时,字符串“abc”现在将返回标记 a、b 和 c 而不是仅 a 和 b)并且多个分隔符不会导致空令牌。

【讨论】:

    【解决方案2】:

    我想可以为你调试它,但从长远来看这对你没有帮助。这就是你要做的。

    在每一行之后,放置一个 printf() 或 cout 语句,将更改的变量转储到标准输出。然后运行您的代码,将一组简单的参数传递给它:

    vector<string> x = split ("Hello there, Bob.", ' ');
    

    然后,检查输出以了解您的实现为何不起作用。你可能不得不跳出代码,因为如果它只是坐在那里,你可能已经让自己陷入了那些新奇的无限循环中。

    给一个人一条鱼,他会吃一天,教一个人钓鱼,他永远不会再饿了。

    或特里普拉切特版本:

    给一个人点火,他会温暖一天,让一个人着火,他会温暖他的余生。

    更新:

    既然你说你实际上已经按照我的建议做了,这就是从这样做中发现的。很明显,当您在while 循环的末尾将beg 设置为temp 时,它指向的是空格。这是通过在while 循环顶部打印beg 字符串发现的 - 在提取第一个单词后它从未改变。

    然后,当您执行下一个find 时,它会找到完全相同的空格,而不是先跳过空格然后正确调用find。您需要跳过每个 find 之后的空格,确保不会迭代超出字符串的末尾。

    这是我的解决方案。随心所欲地使用它。

    #include <iostream>
    #include <string>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    vector<string> split( const string &str, const char &delim ) {
        typedef string::const_iterator iter;
        iter beg = str.begin();
        vector<string> tokens;
    
        while(beg != str.end()) {
            //cout << ":" << beg._Myptr << ":" << endl;
            iter temp = find(beg, str.end(), delim);
            if(beg != str.end())
                tokens.push_back(string(beg, temp));
            beg = temp;
            while ((beg != str.end()) && (*beg == delim))
                beg++;
        }
    
        return tokens;
    }
    
    int main () {
        vector<string> x = split ("Hello, my name is Bob. ", ' ');
        return 0;
    }
    

    如果没有 while 循环末尾的空格跳过代码,输出是:

    :Hello, my name is Bob. :
    : my name is Bob. :
    : my name is Bob. :
    : my name is Bob. :
    : my name is Bob. :
    : my name is Bob. :
    : my name is Bob. :
    : my name is Bob. :
    

    等等,无穷无尽。 使用跳过代码,你会得到:

    :Hello, my name is Bob. :
    :my name is Bob. :
    :name is Bob. :
    :is Bob. :
    :Bob. :
    

    【讨论】:

    • 是的,这正是我在发布之前所做的,抱歉没有说明......由于某种原因,它处于无限循环中,我认为这是因为 Charles Bailey 指出的......我试过了beg = ++temp 以便 beg 指向分隔符后的字符,但仍会被炸掉..
    • 我也喜欢,“给一个人一条鱼,他会吃一天。给他一个 Playstation,他不会打扰你几个星期”。
    【解决方案3】:

    您的 while 循环中存在一个问题,如果找到分隔符,那么 temp 将指向第一个 find 调用之后的第一个分隔符。

    在 while 循环结束时,您将 beg 设置为 temp 的值。

    现在beg 也指向第一个分隔符。

    当下次调用find 时,它将再次返回beg 的当前值,因为它确实指向一个分隔符。

    temp 没有从之前的值继续移动,因此您处于无限循环中。

    【讨论】:

      【解决方案4】:
      vector<string> split( const string &str, const char &delim )
      {
          typedef string::const_iterator iter;
      
          iter beg = str.begin();
      
          vector<string> tokens;
      
          while(beg != str.end())
          {
              iter temp = find(beg, str.end(), delim);
              if(beg != str.end())
                  tokens.push_back(string(beg, temp));
              if(temp != str.end())
                  temp++;
              beg = temp;
          }
      
          return tokens;
      }
      

      【讨论】:

      • 或者直接说 beg = temp+delim.size();
      • temp++ 可能是拆分字符串的错误方法。这取决于您希望如何连续处理多个分隔符。
      【解决方案5】:

      调试此代码的最简单方法是打印beg 的所有位置。如果beg 没有增加,那就是你的问题。

      【讨论】:

        【解决方案6】:

        除了beg 需要随着分隔符的大小而增加外,还遗漏了一种特殊情况:字符串中没有分隔符的情况。

        【讨论】:

        • 我认为这种情况很好——它将单个标记推入向量中,该标记是整个字符串。
        • 如果 find 没有返回结尾,它只会推送一个令牌。如果字符串中没有分隔符,它将返回结尾。还是不会?
        • 正如所写,代码总是推送一个标记(除非字符串为空),因为令人困惑的是,它测试的是“beg”,而不是 find 的返回值。所以循环中的测试总是正确的。
        【解决方案7】:

        也许这个:

        std::vector<std::string> &mysplit(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;
        }
        

        【讨论】:

          【解决方案8】:

          我必须爱上Boost,因为它也为这个问题提供了一个方便的解决方案:

          
          std::vector<std::string> Split(const std::string &s, const std::string &d)
          {
                  std::vector<std::string> v;
          
                  for (boost::split_iterator<std::string::iterator> i = boost::make_split_iterator(s, boost::first_finder(d, boost::is_iequal()));
                       i != boost::split_iterator<std::string::iterator>();
                       ++i) {
                          v.push_back(boost::copy_range<std::string>(*i));
                  }
          
                  return v;
          }
          

          【讨论】:

            【解决方案9】:

            您不必重新发明轮子,boost 为您提供了string splitting 函数。
            示例代码:

            string stringtobesplit = "AA/BB-CC")
            vector<string> tokens;
            
            boost::split(tokens, stringtobesplit, boost::is_any_of("/-")); 
            // tokens now holds 3 items: AA BB CC
            

            【讨论】:

              【解决方案10】:

              这是另一个基于 Boost 的漂亮而简短的版本,它使用整个字符串作为分隔符:

              std::vector<std::string> result;
              boost::iter_split(result, str, boost::first_finder(delim));
              

              或不区分大小写:

              std::vector<std::string> result;
              boost::iter_split(result, str, 
                  boost::first_finder(delim, boost::is_iequal()));
              

              【讨论】:

              • 如果你已经在使用 boost,我认为这是最好的解决方案(upvote)
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-05-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多