【问题标题】:Why does assertion failed if both values are the same?如果两个值相同,为什么断言会失败?
【发布时间】:2015-07-08 19:50:44
【问题描述】:
string removeNonAlphas(string original)
{
    for(int i = 0; i < original.size(); ++i){
        if(!(original[i] > 64 && original[i] < 91) &&
           !(original[i] > 96 && original[i] < 124)){
            original[i] = original[i] - original[i];
        }
    }
    return original;
}

//test1.cpp

string test = "abc abc";
cout << removeNonAlphas(test) << endl; // output = "abcabc"
assert(removeNonAlphas(test) == "abcabc"); // assertion failed

//上面的断言为什么会失败? removeNonAlphas 结果(“abcabc”)与 //rhs "abcabc"

【问题讨论】:

  • 你为什么希望original[i] = original[i] - original[i];删除任何东西?
  • 正如上面的人所说,他引用的那行只是将索引 i 处的值重新分配为 0(本身 - 本身 = 0)。这不是删除。
  • @user2357112:因为他测试了函数的结果,发现它给出了他期望的结果。问题是测试存在缺陷,新程序员可能不知道这是可能的。

标签: c++ string assertion


【解决方案1】:
original[i] = original[i] - original[i];

这使得它用'\0' 替换字符,但不会删除它。因此,输出不是"abcabc",而是"abc\0abc"'\0' 是不可打印的,因此您不会在输出中看到它,但当您将其与 == 进行比较时,它就会出现。

不要替换字符串中的字符,而是在迭代旧字符串时创建一个新字符串:

string removeNonAlphas(string const& original)
{
    std::string result;
    for(char c : original)
       if((c > 64 && c < 91) ||
          (c > 96 && c < 124))
           result.push_back(c);
    return result;
}

注意:更喜欢使用std::isalpha 而不是硬编码值。

【讨论】:

    【解决方案2】:

    两个值不一样,但区别是非打印字符,所以cout和肉眼看不出有什么区别。

    尝试使用合适的工具,例如调试器,您会在函数结果中看到额外的\0 字符。

    【讨论】:

      【解决方案3】:

      您实际上并没有擦除字符串中的任何字符。您只是为它们分配了值 0。它看起来就像它工作 - 这是最糟糕的。 '\0' 只是一个不可打印的字符,这就是它看起来打印相同的原因。 == 实际上会检查每个字符,甚至是不可打印的字符,所以它会捕捉到你看不到的内容。

      谢天谢地,string 类通过提供这样的成员函数使erase 字符变得容易:

      original.erase(i, 1); // erase a single character starting at i
      

      现在仅此还不够。你删除了一个字符,现在i 正在“指向”下一个元素——但你不会检查它。如果我们有"abc12abc",在删除1 之后,我们会跳过2。所以我们需要改变我们的迭代方式:

      for (std::string::iterator it = original.begin();
           it != original.end();
           /* nothing */)
      {
          // here's a better way to do checking
          if (!(*it >= 'A' && *it <= 'Z') &&
              !(*it >= 'a' && *it <= 'z'))
          {
              // erase(iterator ) will return the next iterator
              it = original.erase(it);
          }
          else
          {
              ++it;
          }
      }
      

      那行得通。它也非常冗长。并且容易出错。这就是为什么我们有擦除删除习语:

      original.erase(
          std::remove_if(original.begin(),
                         original.end(),
                         [](char c) { return !std::isalpha(c); }),
          original.end()
      );
      return original;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-19
        • 2020-12-30
        • 1970-01-01
        • 2011-02-28
        • 1970-01-01
        相关资源
        最近更新 更多