C语言 为什么计数器变量出现一个单词的缩写?

bjp0bcyl  于 2023-03-07  发布在  其他
关注(0)|答案(5)|浏览(131)

字数总是少一个。我试着在i < len;上添加+1 - +10我试着删除“Text:“。例如,编译提示条目“One fish. Two fish. Red fish. Blue fish.”,字母的输出是正确的,29个字母,单词的输出是WRONG(相差1)- 7个字(应为8),句子的输出也是正确的-- 4个句子。我假设原型int count_words(string sample)中的isblank不因为最后一个字以句点结束,所以不要计数最后一个字。
将计数器设置为1而不是0是个好主意,还是有更好的方法来获得最后一个字?

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

int count_letters(string sample);
int count_words(string sample);
int count_sentences(string sample);

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

    int lettercount = count_letters(sample);
    printf("%i letters\n", lettercount);

    int wordcount = count_words(sample);
    printf("%i words\n", wordcount);

    int sentencescount = count_sentences(sample);
    printf("%i sentences\n", sentencescount);
}

int count_letters(string sample)
{
    int counter = 0;
    int len = strlen(sample);
    for (int i = 0; i < len; i++)
    {
        if (isalpha(sample[i]))
        {
            counter++;
        }
    }
    return counter;
}

int count_words(string sample)
{
    int counter = 0;
    int len = strlen(sample);
    for (int i = 0; i < len; i++)
    {
        if (isblank(sample[i]))
        {
            counter++;
        }
    }
    return counter;
}

int count_sentences(string sample)
{
    int counter = 0;
    int len = strlen(sample);
    for (int i = 0; i < len; i++)
    {
        if ((sample[i] == '.') || (sample[i] == '?') || (sample[i] == '!'))
        {
            counter++;
        }
    }
    return counter;
}
3ks5zfa0

3ks5zfa01#

把计数器设为1而不是0是个好主意吗......
将计数器设置为1是错误的,因为字符串可能没有单词。
OP的方法并不适用于字符串可以选择以非字母开始/结尾的所有情况。

  • 对于is...()函数,最好使用unsigned char值,因为这些函数没有为大多数负值定义。
  • 一个非常长的字符串可能超过INT_MAX的长度。最好用size_t来计算字母/单词,以处理所有字符串。
  • 不是迭代到字符串的长度,而是迭代到阅读到一个 *null字符 * 为止。这跳过了前面不必要的(而且代价很高的)strlen()调用。

......还是有更好的方法来做最后的决定?
一个简单的方法来计算单词:计算从非字母到字母的转换
比许多其他方法更不复杂和更不容易出错。

size_t count_words(const char *str) {
  const unsigned char *us = (const unsigned char*) str;
  size_t words = 0;
  bool previous_is_letter = false;

  while (*us) {
    bool current_is_letter = isalpha(*us++);
    if (!previous_is_letter && current_is_letter) words++;
    previous_is_letter = current_is_letter;
  }

  return words;
}

计算字母数只需要在字符串中传递1次,而不是像OP的代码那样传递2次。

  • is...()函数返回0或非零值。使用!!可将任何非零值转换为1。
size_t count_letters(const char *str) {
  size_t letters = 0;
  const unsigned char *us = (const unsigned char*) str;
  while (*us) {
    letters += !!isalpha(*us);
    us++;
  }
  return letters;
}

计算句子也是类似的。

// letters += !!isalpha(*us);
sentences += *us == '.' || *us == '?' || *us == '!';
ut6juiuv

ut6juiuv2#

在你的count_words()函数中,你没有正确地考虑前导空格、多个包含的空格字符和尾随空格的存在与否,本质上你把每个空格当作一个单词。
虽然您可以使用库函数处理此问题(使用scrcspn()/strspn()的@Fe2O3是一个很好的例子),使用简单的循环 State-Variable 来跟踪您是在单词内阅读字符还是在单词外读取空格,这是另一种非常有效的方法(也可以很好地操作字符串文字/常量字符串。)跟踪事物的状态适用于许多问题。
对于单词计数,您可以做一些简单的事情,如下所示,使用in_word作为状态变量,跟踪您是在单词中阅读字符,还是在单词之前、之间或之后读取空格,例如:

/* returns the number of whitespace separated words in str */
size_t count_words (const char *str)
{
    size_t words = 0;
    int in_word = 0;

    while (*str) {
        if (isspace ((unsigned char)*str))
            in_word = 0;
        else {
            if (!in_word)
                words++;
            in_word = 1;
        }
        str++;
    }

    return words;
}

(**注:**字数words仅在从非词内转换到词内时递增,在每个词中的第一个字符触发)
无耻地借用@Fe203的例子,加上几个额外的空格字符,并将字符串分成两部分,可能是:

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

/* returns the number of whitespace separated words in str */
size_t count_words (const char *str)
{
    size_t words = 0;
    int in_word = 0;

    while (*str) {
        if (isspace ((unsigned char)*str))
            in_word = 0;
        else {
            if (!in_word)
                words++;
            in_word = 1;
        }
        str++;
    }

    return words;
}

int main (void) {
    
    printf ("words = %zu\n", count_words ( "One fish Two   fish Red Fish Blue Fish" ) );
    printf ("words = %zu\n", count_words ( "  One fish Two fish Red Fish Blue Fish" ) );
    printf ("words = %zu\n", count_words ( "One fish Two fish Red Fish Blue Fish   " ) );
    printf ("words = %zu\n", count_words ( "  One fish Two fish "
                                           "Red Fish Blue Fish " ) );

    return 0;
}

结果是一样的,每个字符串8个单词。看一遍,如果你有问题就告诉我。

xxhby3vn

xxhby3vn3#

OP代码中的问题假定单词之间只有一个空格,而没有考虑字符串末尾没有空格的情况。
不需要复制或重新创建标准库函数,下面的代码似乎可以工作。

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

int cntWords( char *str ) {
    int cnt = 0;

    str += strspn( str, " " );  // discard leading spaces
    while( *str ) {
        cnt++;
        str += strcspn( str, " " ); // find next SP
        str += strspn( str, " " ); // find next that is not SP
    }

    return cnt;
}

int main( void ) {
    printf( "words = %d\n", cntWords( "One fish Two fish Red Fish Blue Fish" ) );
    printf( "words = %d\n", cntWords( "  One fish Two fish Red Fish Blue Fish" ) );
    printf( "words = %d\n", cntWords( "One fish Two fish Red Fish Blue Fish   " ) );
    printf( "words = %d\n", cntWords( "  One fish Two fish Red Fish Blue Fish " ) );

    return 0;
}
words = 8
words = 8
words = 8
words = 8
    • 编辑**

该函数可以做得更紧凑:

int cntWords( char *str ) {
    int cnt = 0;

    while( *str ) {
        str += strspn( str, " " );  // find next non-space
        cnt += *str != '\0';
        str += strcspn( str, " " ); // find next space
    }

    return cnt;
}
nimxete2

nimxete24#

函数count_words不考虑最后一个字,因为它后面没有空格。
此外,如果单词之间有多个空格字符,或者字符串包含前导空格或尾随空格,则通常该函数将错误地计算单词数。
而且使用函数strlen是低效和冗余的。
该函数可按以下方式定义。

int count_words( string sample )
{
    int counter = 0;

    while ( *sample )
    {
        while ( isblank( ( unsigned char )*sample ) ) ++sample;

        if ( *sample )
        {
            ++counter;
            while ( *sample && !isblank( ( unsigned char )*sample ) ) ++sample;
        }
    }

    return counter;
}

尽管这样声明函数会更好

size_t count_words( const char *sample )
{
    size_t counter = 0;

    while ( *sample )
    {
        while ( isblank( ( unsigned char )*sample ) ) ++sample;

        if ( *sample )
        {
            ++counter;
            while ( *sample && !isblank( ( unsigned char )*sample ) ) ++sample;
        }
    }

    return counter;
}

这是一个演示程序。

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

size_t count_words( const char *sample )
{
    size_t counter = 0;

    while (*sample)
    {
        while (isblank( ( unsigned char )*sample )) ++sample;

        if (*sample)
        {
            ++counter;
            while (*sample && !isblank( ( unsigned char )*sample )) ++sample;
        }
    }

    return counter;
}

int main( void )
{
    const char *sample = "One fish. Two fish. Red fish. Blue fish.";

    printf( "There are %zu words.\n", count_words( sample ) );
}

程序输出为

There are 8 words.

函数count_sentences也不正确。例如,对于字符串"What?!",该函数返回两个句子而不是一个句子。
该函数可以以与上面所示的函数count_words相同的方式实现。

h9a6wy2h

h9a6wy2h5#

这可能会起作用,也许计数器从1开始,如果len等于0,则返回0?您正在计数空格,而不是单词。因此,对于字符串“Hello World”,您将计数1,因为有1个空格。

int count_words(string sample)
{
    int counter = 1;
    int len = strlen(sample);
    if(len == 0) {
        return 0;
    }
    for (int i = 0; i < len; i++)
    {
        if (isblank(sample[i]))
        {
            counter++;
        }
    }
    return counter;
}

我会用strtok来做,真的没有别的原因,只是它对我来说计算实际的单词,它实际上更复杂。

int count_words(const char *sample)
{
    char *cpy = NULL;
    char *token = NULL;
    int word_count = 0;

    /* I believe strtok will mangle the string so I'll make a copy of it */
    cpy = calloc(1, strlen(sample) + 1);
    if(!cpy) {
        return -1;
    }
    strcpy(cpy, sample);

    token = strtok(cpy, " \t");
    while(token) {
        word_count++;
        token = strtok(NULL, " \t");
    }
    free(cpy);
    return word_count;
}

相关问题