提问者:小点点

在C++中查找(奇怪的)字符串中的单词


这个程序在技术上有什么问题? 预期的结果是6,因为这是字符串中存在的总字数。

#include <iostream>
using namespace std; 

int main()
{
    string str = "  Let's   count     the      number     of    words  ";
    int word = 0;
    for (int i = 0; str[i] != '\0';)
    {
        if ((str[i] == 32 && str[i + 1] == 32) || (str[i] == 32 && str[i - 1] == 32))
        {
            ++i;
        }
        else if ((str[i] == 32 && str[i - 1] != 32) || (str[i] == 32 && str[i + 1] != 32))
        {
            word++;
        }
        ++i;
    }
    cout << "No. of words: " << word << endl;
    return 0;
}

我的错误结果:

No. of words: 0

另外,如果我尝试将字符串中的空格甚至字符串本身更改为一组全新的带空格的单词,例如:

string str = "   Hello world   ";
string str = "Hello    world! How   are you?   ";

我仍然得到不正确的结果,但不同于0。 我是C++编程的新手,这些奇怪的行为让我做噩梦。 这种情况普遍吗? 我能做些什么来纠正这个问题?

如果您能突出显示或纠正我的程序的方式我写它,这将是非常有帮助和快速的我理解的错误,而不是必须知道一些新的命令在这一点上。 因为,正如我所说的,我完全是C/C++的初学者。

谢谢你的时间!


共2个答案

匿名用户

我想你有办法做到这一点。 看看这段代码。 和你的非常相似:

string s = "  Let's   count     the      number     of    words  ";

int word = 0;

for (auto i = 0; s[i] != '\0'; i++) {
    if (i == 0) {
        if (s[i] != ' ') {
            ++word;
        }
        continue;
    }

    if (s[i - 1] == ' ' && s[i] != ' ') {
        ++word;
    }
}

cout << "No of Words: " << word << endl;

其思想是对字符串进行迭代,逐个字符地读取。 所以我们做一些逻辑:

  • 如果我们在第一个字符串字符中并且它等于“”,则转到下一个循环迭代
  • 如果我们在第一个字符串字符中,并且它与“”不同,则表示我们正在开始一个单词,因此计数它并跳到下一个循环迭代。
  • 如果我们到达第二个if,意味着我们不在第一个位置,因此尝试访问i-1应该是有效的。 然后我们只检查前一个字符是否是空白,而当前的不是。 这意味着我们要开始一个新词。 因此对其进行计数并跳到下一个循环迭代。

另一种更简单的方法是使用StringStream:

string s = "  Let's   count     the      number     of    words  ";
stringstream ss(s);
string sub;
int word = 0;
while (ss >> sub) {
    ++word;
}
cout << "No of Words: " << word << endl;

这样基本上就是从字符串中逐字提取。

匿名用户

我是C++编程的新手,这些奇怪的行为让我做噩梦。 这种情况普遍吗?

是的,很常见。 您编写了一堆逻辑,却没有工具来理解它的行为。

我能做些什么来纠正这个问题?

您可以从两个方向着手:

>

  • 对此进行调试,以便更好地了解它的操作方式:

    • 在每行中预先确定您希望它对一些短输入执行什么操作
    • 在调试器中单步遍历它以查看它实际执行的操作
    • 想想为什么它没有达到您预期的效果

    有时问题在于您的代码没有正确地实现您的算法,有时算法本身也被破坏了,而且经常是两者兼而有之。 通过这两个方面的工作将会给你一些洞察力。

    编写一开始就更容易理解的代码(以及等效地,编写易于推理的算法)。

    这取决于您对某件事是否容易推理有一些直觉,这是您从迭代步骤1中开发出来的。

    …而不是在这个时候必须知道一些新的命令。

    不管怎样,您都需要学习使用调试器,所以现在是开始调试的好时机。

    我们当然可以改进现有的代码,尽管我更愿意修改逻辑。 一般来说,我鼓励您将现有的if条件抽象成一些小函数,但问题是它们目前似乎没有任何意义。

    那么,我们该如何定义一个词呢?

    您的代码说它至少是一个前或后有空格的非空格字符。 (顺便说一句,一定要更喜欢'而不是32,而且std::isspace比两者都好。)

    但是,代码的隐含定义有问题,因为:

    • 每个长于一个字符的单词都有第一个和最后一个字符,您将对每个字符进行计数
    • 您无法检查第一个字符前面是否有任何内容,否则会超出界限
    • 最后一个字符后跟空终止符,但不能将其算作空格

    让我们选择一个不同的定义,它不需要读取str[i-1],也不需要当前代码出错时的复杂遍历。

    我声称一个单词是一个非空格字符的连续子串,单词由空格字符的连续子串分隔。 因此,我们可以编写伪代码,而不是查看每对连续的字符:

        for (current = str.begin(); current != str.end(); ) {
            // skip any leading whitespace
            current = find_next_non_whitespace(str, current);
            if (current != str.end()) {
                // we found a word
                ++words;
                current = find_next_whitespace(str, current);
            }
        }
                
    

    注意:当我谈到将代码抽象成一些小函数时,我指的是find_next_non_whitespace-它们应该实现起来很简单,易于测试,并且有一个能告诉你一些事情的名称。

    当我说你现有的条件似乎没有意义的时候,那是因为替换

    if ((str[i] == 32 && str[i + 1] == 32) || (str[i] == 32 && str[i - 1] == 32))
    

    用,比如说,

    if (two_consecutive_spaces(str, i))
    

    提示的问题比回答的问题多。 为什么有一个特殊情况正好是两个连续的空格? 只是一个空间不同吗? 如果我们有两个单词,它们之间只有一个空格,实际会发生什么? 为什么我们在这种情况下前进了两个字符,但在单词branch上只前进了一个?

    代码不能很容易地映射回可解释的逻辑是一个不好的迹象--即使它工作了(我们知道它没有),我们也不能很好地理解它,无法更改,扩展或重构它。

  • 相关问题


    MySQL Query : SELECT * FROM v9_ask_question WHERE 1=1 AND question regexp '(c++|中|查找|奇怪|字符串|中|单词)' ORDER BY qid DESC LIMIT 20
    MySQL Error : Got error 'repetition-operator operand invalid' from regexp
    MySQL Errno : 1139
    Message : Got error 'repetition-operator operand invalid' from regexp
    Need Help?