【问题标题】:c++ char* parsingc++ char* 解析
【发布时间】:2013-02-16 17:11:52
【问题描述】:

所以,我正在尝试编写一个返回vector<char**> 的函数,例如:

vector<char**> test(string mystr) {

  char*temp=new char[mystr.size()+1];

  strcpy(temp,mystr.c_str());

  char*subStr=strtok(temp,":");

  while(subStr!=NULL) {

    int i=0;

    char**args=new char*[200];

    char*tempsta=newchar[strlen(subStr)+1];

    strcpy(tempsta, subStr);

    args[i]=strtok(tempsta," ");

    while(args[i]!=NULL) {

      i++;

      args[i]=strtok(NULL," ");

    }

    fullVec.push_back(args);

    //cout<<subStr<<endl;

    subStr=strtok(NULL,":");

  }
return fullVec;
}

所以我想将split 参数字符串与":" 分隔符,然后与“”分隔符。在cout&lt;&lt;subStr 调用中,如果我注释掉从int i=0fullVec.push_back(args) 的所有内容,我会得到预期结果。如果我不注释掉所有这些行,我只会得到第一个 substring(直到遇到第一个“:”),然后最大的 while 循环退出。

我的意思是预期的;让我们假设参数是“我的名字是:bon jovi:xxx ab” 如果所有内容都被注释掉,将打印以下行:

my name is
 bon jovi
 xxx ab

如果我保持原样,将会发生的只是

my name is

将打印,大循环将退出

感谢任何帮助,谢谢! (是的,我知道这似乎是一个愚蠢的练习,可以更优雅/更轻松地完成......但是我希望在使用字符串等娱乐之前让这个解决方案起作用。)

【问题讨论】:

  • 混合使用 C 和 C++ 会产生令人讨厌的坏代码
  • 你没有释放你的newd 内存任何...
  • 只要是写C++代码,就尽量避免写C风格的代码。如果您没有充分的理由使用char*,那么请改用std::string
  • -nneonneo你是对的,只是想在继续之前弄清楚这个问题--LiHo,我理解你的意思,但是我将无法一直使用常规方法学习试图“探索”
  • @DaxDurax:你想学就够了。但是你应该分别学习 C 和 C++。它们是两种不同的语言。像这样混合它们只会导致混淆难以阅读的代码。我将重写为 C 解决方案和不同的 C++ 解决方案。因为这两种技术完全不同。

标签: c++ arrays vector char


【解决方案1】:

您的问题是 strtok() 在调用之间保持状态。

如果第一个参数不为 NULL,则它用于重置状态,否则它使用已保存的状态从中断处继续解析。

由于您对 strtok() 有两个嵌套调用,因此第二个调用会干扰外部调用的状态。

这个电话:

args[i]=strtok(tempsta," ");

正在重置 strtok() 的内部状态。它现在不再知道你的外部调用中的任何状态。因此,当您到达内部循环中的字符串末尾时。

这个电话:

subStr=strtok(NULL,":");

现在正在使用内部循环的已保存状态。所以它基本上只是在您已经到达该标记化流的末尾时终止。

【讨论】:

  • 我想这在某种令人费解的方式中是有道理的......你有什么建议可以在保持封闭循环结构的同时解决这个问题吗?我在网上看到的每个解决方案都建议分离循环
  • 更直接地说,strtok 修改了你的 C 风格字符串。
  • @ThomasMatthews:这绝对是一件值得关注的事情(也是关于 strtok() 的坏事之一)。但不是这里出现任何问题的原因。
  • @DaxDurax:不。如果您使用的是 strtok(),则需要分离循环。您可以使用strtok_r(),它允许您为每个标记化流使用单独的上下文。
  • 感谢 loki strtok_r 正是我想要的。这里的回答都非常有用,我现在必须把它们全部收起来!
【解决方案2】:

正如Loki 已经完美指出的那样,您不应该混合使用 C 和 C++。如果您需要 C++ 解决方案来解决您的问题,那么最好坚持使用 STL 类来为您处理丑陋的内存管理(请参阅 RAII idiom),例如 std::stringstd::vectorstd::istringstream

这就是你的函数的样子:

typedef std::vector<std::string> Line;

std::vector<Line> parse(std::string inputString)
{
    std::vector<Line> lines;
    std::istringstream inputStream(inputString);
    for (std::string line; std::getline(inputStream, line, ':'); )
    {
        if (!line.empty())
        {
            lines.push_back(Line());
            std::istringstream lineStream(line);
            for (std::string word; std::getline(lineStream, word, ' '); )
            {
                if (!word.empty())
                    lines.back().push_back(word);
            }
        }
    }
    return lines;
}

使用示例:

std::vector<Line> lines = parse("my name is: bon jovi: xxx ab");

for (int li = 0; li < lines.size(); ++li)
{
    for (int wi = 0; wi < lines[li].size(); ++wi)
        std::cout << lines[li][wi] << "_";
    std::cout << std::endl;
}

输出

my_name_is_
bon_jovi_
xxx_ab_

希望这会有所帮助:)

【讨论】:

    【解决方案3】:

    正如 cmets 中所述,您正在混合 C 风格和 C++ 风格的代码,这会导致相当混乱。除非您有“充分的理由”诉诸 char* 而不是 std::string,否则最好使用 stlboost

    boostway:

    std::string delims = " :";
    boost::split(vector, mystr, boost::is_any_of(delims));
    

    stl 方式:

      vector<string> result;
      std::string delims = " :";
      std::istringstream ss( mystr );
      while (!ss.eof())
      {
        getline( ss, field, delims);
        if ((empties == split::no_empties) && field.empty()) continue;
        result.push_back( field );
      }
    

    有关更多方法和良好比较,请参阅cplusplus article

    【讨论】:

    • 在限制boost使用的问题中没有提到,我也添加了一个stl替代
    • "但是我想让这个解决方案工作before I entertain using string etc" :)
    • 我刚刚获得了我的 POV。我没有因为这个而对你投反对票。但这可能会阻止其他人对你投赞成票。 :)
    • 我怀疑它是在我开始写答案后添加的,但我可能错过了。
    • 我的立场是正确的。我的解决方案与这个问题不太相关
    猜你喜欢
    • 1970-01-01
    • 2021-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多