2

我正在尝试编写一个计算字符串中多个元素的程序。其中第一个是字母。

该作业是 CS50 第 2 周问题集的一部分,因此包含库。

使用while条件我能够计算每个字符,但是一旦我添加了代码就停止工作isalnum(它检查字符是否是字母数字)。

我究竟做错了什么?

#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>

int main(void) {
    string text = get_string("Text: ");
    printf("%s, \n", text);

    int letters = 0;
    while (text[letters] != '\0') {
        if (isalnum(text[letters])) {
            letters++;
        }
    }
    printf("%i \n", letters);
}
4

5 回答 5

2

如果您只想计算字母:

size_t count_letters(const char *str)
{
    size_t count = 0;

    while(*str)
    {
        count += !!isalpha(*str++);
    }

    return count;
}

或者如果你更喜欢for循环

size_t count_letters_for_loop(const char *str)
{
    size_t count = 0;

    for(; *str; str++)
    {
        count += !!isalpha(*str);
    }

    return count;
}

正如大卫建议的那样:

!!操作在逻辑上否定该值的两倍。由于任何逻辑运算都会给出 0 或 1,因此这种双重否定给出1了任何非零值或0零。需要它作为 isalpha 函数Returns non-zero value if c is an alphabet, else it returns 0.

尝试将这样的逻辑移动到单独的函数中。这是C语言中一项非常重要的技能

于 2020-05-07T20:21:22.370 回答
2

这里展示了如何定义正确的循环

size_t letters = 0;
for ( size_t i = 0; text[i] !='\0'; i++ )
{
    if ( isalnum( ( unsigned char )text[i] ) )
    {
       letters++;
    }
}

printf( "%zu\n", letters );

如果你想使用一个while循环,那么它可以看起来像

size_t letters = 0;
size_t i = 0;
while ( text[i] !='\0' )
{
    if ( isalnum( ( unsigned char )text[i++] ) )
    {
       letters++;
    }
}

printf( "%zu\n", letters );

请注意该功能isalnum检测字母和数字。如果您只需要计算字母,请使用该功能isalpha

于 2020-05-07T20:05:58.777 回答
1

正如您所发现的,由于并非字符串中的所有字符都保证是字母数字字符,因此您将其letters用作计数器和索引是有缺陷的。当您遇到一个不是 alpha 也不是数字的字符时,if (isalnum(text[letters]))测试为 false 并且letters永远不会增加,从而导致此时出现无限循环。(你在下一次迭代中再次测试同一个角色——结果相同——而且风景永远不会改变......)

正如所有其他非常好的和非常正确的答案所建议的那样,只需使用一个单独的循环计数器变量(或指针)并递增它来迭代你的字符串。

您可能会做的另一件事来验证您的逻辑(在输出isalnum()字符时)是简单地取消重新打印原始字符串printf(您的条目就在您面前),而是输出与您的条件匹配的字符. 例如:

    for (int i = 0; text[i]; i++) {
        if (isalnum((unsigned char)text[i])) {
            putchar (text[i]);
            letters++;
        }
    }

这只是您的输出的一个微小变化,它提供双重职责,提供您的输出,并提供与所用标准匹配的每个字符的确认。

另请注意,没有必要,#include <string.h>因为在您的代码中没有需要包含它的功能。通过这些更改,一个简单的示例可能是:

#include <cs50.h>
#include <stdio.h>
#include <ctype.h>

int main(void) {

    int letters = 0;
    string text = get_string("Text: ");

    for (int i = 0; text[i]; i++) {
        if (isalnum((unsigned char)text[i])) {
            putchar (text[i]);
            letters++;
        }
    }

    printf (", %d\n", letters);
}

示例使用/输出

$ ./bin/ltrcountcs50-1
Text: 123.abc-456_def_*.*_789
123abc456def789, 15

将计数移动到函数中

您可以轻松地将字母数字字符的计数移动到函数中。将字符串传递给函数的一个好处是函数接收到的指针是指针的副本main()(C 是按值传递)。这使您可以简单地使用参数进行迭代并返回字母数字字符的计数,例如

int countalnum (const char *s)
{
    int letters = 0;

    while (*s)
        if (isalnum((unsigned char)*s++))
            letters++;

    return letters;
}

注意:您必须传递参数 asconst char *以使用指向常量 char 的指针。您不能使用const stringcs50 typedef。如果您不更改函数中传递的值,传递 asconst允许编译器进行优化,否则不会能够制作)

使用上面的函数,您的代码可以简化为:

#include <cs50.h>
#include <stdio.h>
#include <ctype.h>

int countalnum (const char *s)
{
    int letters = 0;

    while (*s)
        if (isalnum((unsigned char)*s++))
            letters++;

    return letters;
}

int main(void) {

    string text = get_string("Text: ");

    printf ("%s, %d\n", text, countalnum(text));
}

示例使用/输出

$ ./bin/ltrcountcs50-1
Text: 123.abc-456_def_*.*_789
123.abc-456_def_*.*_789, 15

但是在这里,使用该函数会导致将整个原始字符串打印在main(). 您可以根据需要调整输出。

于 2020-05-07T21:19:00.577 回答
1

while的循环有一个缺陷:您使用相同的变量来计算字母数字字符并为字符串编制索引。它仅在所有字符都是字母数字的情况下才有效,否则您将获得无限循环,因为您停止递增letter.

您应该使用for带有 index 的循环i

另请注意,对于负值,isalnum()所有来自的函数都未定义,除了. 在默认签名的平台上,字符串中的某些字符可能具有负值,如果将它们传递给. 您应该转换值以避免这种情况。<ctype.h>EOFcharisalnum()char(unsigned char)

这是修改后的版本。

#include <cs50.h>
#include <stdio.h>
#include <ctype.h>

int main(void) {
    string text = get_string("Text: ");
    printf("%s, \n", text);

    int letters = 0;
    for (int i = 0; text[i] != '\0'; i++) {
        if (isalnum((unsigned char)text[i])) {
            letters++;
        }
    }
    printf("%i\n", letters);
    return 0;
}
于 2020-05-07T20:51:09.293 回答
0

你说是字母,然后你用isalnum?因此,在您的情况下,数字将被计为字母,并且您增加循环计数的字母的值,仅当它是字母时才迭代字符串,以便在条件为假时导致无限循环,这是一个解决方案:

#include <stdio.h>
#include <ctype.h>

int countLetters(char *str) {
  int letters = 0, ind = 0;
  while(str[ind] != '\0') 
    isalpha((unsigned char)str[ind++]) && letters++;
  return letters;
}

int main(void) {
  char text[100];
  fgets(text, sizeof(text), stdin);
  printf("%i", countLetters(text));
  return 0;
}
// input: hello 15-p */x
// output: 7
于 2020-05-07T20:16:02.100 回答