arrayscloops

For loop doesnt iterate over all string characters? (BEGINNER QUESTION)


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

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

int main(void)
{
    string text = get_string("Type in the text: ");

    int n1 = count_letters(text);
    int n2 = count_words(text);
    int n3 = count_sentences(text);

    printf("Letters: %i\n", n1);
    printf("Words: %i\n", n2);
    printf("Sentences: %i\n", n3);
}


int count_letters(string text)

{
    int letters = 0;
    for (int i = 0; i < text[i]; i++)
    {
        if (text[i] >= 'a' && text[i] <='z')
        {
            letters++;
        }
        else if (text[i] >= 'A' && text[i] <= 'Z')
        {
            letters++;
        }
        else
        {
            letters = letters + 0;
        }
    }
    return letters;
}


int count_words(string text)
{
    int words = 1;
    for (int i = 0; i < text[i]; i++)
    {
        if (text[i] == ' ')
        {
            words++;
        }
        else
        {
            words = words + 0;
        }
    }
    return words;
}

int count_sentences(string text)
{
    int sentences = 0;
    for (int i = 0; i < text[i]; i++)
    {
        if (text[i] == '!' || text[i] == '?' || text[i] == '.')
        {
            sentences++;
        }
        else
        {
            sentences = sentences + 0;
        }
    }
    return sentences;
}

When I run this and input for example: "aa aa aa aa aa aa aa aa aa bb." it returns correct values i.e. letters = 20, words = 10 and sentence = 1.

However, when I try to input "aaa aaa aaa aaa aaa aaa aaa aaa aaa bbb." it looks like it cuts the iteration at text[26]. I.e. it returns letters = 27, words = 9, sentences = 0.

Any suggestions on what might be causing this and how to fix it? Thank you.


Solution

  • I tried out your code, and the main issue is in the "for" loop testing of the upper limit value. To illustrate this moving target issue, I added a "printf" statement to track how the value that is being checked for "i < text[i]" changes and ultimately produces the undesired outcome in the various counts.

    int count_letters(char *text)
    
    {
        int letters = 0;
        for (int i = 0; i < text[i]; i++)
        {
            printf("i: %d  text[i]: %d  value for text[i + 1]: %d\n", i, text[i], text[i+1]);
    

    Executing your code with the longer sentence example yields the following output.

    craig@Vera:~/C_Programs/Console/Sentences/bin/Release$ ./Sentences 
    Type in the text: aaa aaa aaa aaa aaa aaa aaa aaa aaa bbb.
    i: 0  text[i]: 97  value for text[i + 1]: 97
    i: 1  text[i]: 97  value for text[i + 1]: 97
    i: 2  text[i]: 97  value for text[i + 1]: 32
    i: 3  text[i]: 32  value for text[i + 1]: 97
    i: 4  text[i]: 97  value for text[i + 1]: 97
    i: 5  text[i]: 97  value for text[i + 1]: 97
    i: 6  text[i]: 97  value for text[i + 1]: 32
    i: 7  text[i]: 32  value for text[i + 1]: 97
    i: 8  text[i]: 97  value for text[i + 1]: 97
    i: 9  text[i]: 97  value for text[i + 1]: 97
    i: 10  text[i]: 97  value for text[i + 1]: 32
    i: 11  text[i]: 32  value for text[i + 1]: 97
    i: 12  text[i]: 97  value for text[i + 1]: 97
    i: 13  text[i]: 97  value for text[i + 1]: 97
    i: 14  text[i]: 97  value for text[i + 1]: 32
    i: 15  text[i]: 32  value for text[i + 1]: 97
    i: 16  text[i]: 97  value for text[i + 1]: 97
    i: 17  text[i]: 97  value for text[i + 1]: 97
    i: 18  text[i]: 97  value for text[i + 1]: 32
    i: 19  text[i]: 32  value for text[i + 1]: 97
    i: 20  text[i]: 97  value for text[i + 1]: 97
    i: 21  text[i]: 97  value for text[i + 1]: 97
    i: 22  text[i]: 97  value for text[i + 1]: 32
    i: 23  text[i]: 32  value for text[i + 1]: 97
    i: 24  text[i]: 97  value for text[i + 1]: 97
    i: 25  text[i]: 97  value for text[i + 1]: 97
    i: 26  text[i]: 97  value for text[i + 1]: 32
    i: 27  text[i]: 32  value for text[i + 1]: 97
    i: 28  text[i]: 97  value for text[i + 1]: 97
    i: 29  text[i]: 97  value for text[i + 1]: 97
    i: 30  text[i]: 97  value for text[i + 1]: 32
    i: 31  text[i]: 32  value for text[i + 1]: 97
    i: 32  text[i]: 97  value for text[i + 1]: 97
    i: 33  text[i]: 97  value for text[i + 1]: 97
    i: 34  text[i]: 97  value for text[i + 1]: 32   <--- This is point where the subequent "for" loop test will exit.
    Letters: 27
    Words: 9
    Sentences: 0
    

    At the noted point, the next value of "i" will be "35" and will be compared against the value of text[i] which will be a value of "32" (the ascii value for a space). As such, the loop is terminated at that point.

    Utilizing "i < text[i]" would not be the proper test. Ultimately, when parsing through a string, one normally wants to acquire the length of the string and then utilize that value as the limit in whatever character testing is going to happen. Also, the character testing being done is quite correct; however, it probably is a good time to get familiar with the character functions available within the standard "C" include files, specifically "ctype.h".

    With that, following is a refactored version of the code utilizing string and character functionality.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    
    #define SIZE 200
    
    int count_letters(char *text);
    int count_words(char *text);
    int count_sentences(char *text);
    
    int main(void)
    {
        char text[SIZE];                    /* Utilizing conventional character array definition - do not have CS50 */
    
        printf("Type in the text: ");       /* Utilizing conventional string acquisition - do not have CS50         */
        fgets(text, (SIZE - 1), stdin);
    
        for (int i = 0; i < SIZE; i++)
        {
            if (text[i] == '\n')            /* Remove newline character that is returned with fgets function        */
            {
                text[i] = '\0';
                break;
            }
        }
    
        int n1 = count_letters(text);
        int n2 = count_words(text);
        int n3 = count_sentences(text);
    
        printf("Letters: %d\n", n1);
        printf("Words: %d\n", n2);
        printf("Sentences: %d\n", n3);
    }
    
    int count_letters(char *text)
    
    {
        int letters = 0;
        for (int i = 0; i < strlen(text); i++)  /* Standard string length function  */
        {
            if (isalpha(text[i]))               /* Alpha character identification function in ctype.h   */
                letters++;
        }
        return letters;
    }
    
    int count_words(char *text)
    {
        int words = 1;
        for (int i = 0; i < strlen(text); i++)
        {
            if (text[i] == ' ')
                words++;
        }
        return words;
    }
    
    int count_sentences(char *text)
    {
        int sentences = 0;
        for (int i = 0; i < strlen(text); i++)
        {
            if (text[i] == '!' || text[i] == '?' || text[i] == '.')
                sentences++;
        }
        return sentences;
    }
    

    Some points to note.

    • Since I don't have the CS50 library and files on my system, I refactored the code to utilize conventional definitions for character arrays and the prompting of the input data, but there is nothing incorrect with utilizing the CS50 string functionality if you so choose.
    • The "string.h" and "ctype.h" include files were added to provide the needed conventional methodology for performing steps such as acquiring the proper string length ("strlen()") and checking if a character element is an alphabetic character ("isalpha()").

    With those changes, following is the test output at the terminal producing the desired counts.

    craig@Vera:~/C_Programs/Console/Sentences/bin/Release$ ./Sentences 
    Type in the text: aaa aaa aaa aaa aaa aaa aaa aaa aaa bbb.
    Letters: 30
    Words: 10
    Sentences: 1
    craig@Vera:~/C_Programs/Console/Sentences/bin/Release$ ./Sentences 
    Type in the text: Now is the time for all good men to come to the aid of their country.
    Letters: 53
    Words: 16
    Sentences: 1
    

    The key bits to take away are the proper setup of "for" loops, especially as it pertains to the limit testing, being cognizant of the features of strings (character arrays), and the standard capabilities built into the "C" programming include files. Also, along with utilizing the learning tools associated with the CS50 tutorials, it probably also be beneficial to delve into other tutorials as it pertains to loops (for loops, while loops, and so forth) and character arrays.