David Villagran
David Villagran

Reputation: 9

C language - counting number of different vowels with no pointers or additional functions

I got this exercise that I haven't been able to solve, the point is to create a program where you type in a text, then the program analyzes each word of the text and counts the vowels of each word, then the program returns in screen the number of words that have 3 or more different vowels, and by different I mean, it doesn't matter if the word has 3 "a", it only count as one (the word has the vowels "a", it doesn't matter how many times), so for example, the word "above" has 3 vowels, the word "been" has 1 vowels, the word "example" has 2 vowels. The vowels can be upper case or lower case, it doesn't matter, and here is the tricky part: It cannot contain any pointers or functions made by us.

what i did was asking the user to enter word by word so the program analyze each word, and then at the end returns the number of words that contain 3 or more vowels, but I feel like there must be an easier way where the user can type a complete paragraph or text, then the program analyzes each word and return the number of words that have 3 or more different vowels.

Anyway, my code is as follows, any suggestions would be appreciated:

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

main() {

    int vowels, text, words, c, total=0,a=0,e=0,i=0,o=0,u=0;


    printf ("How many words does your text has? ");
    scanf("%d",&words);

    for(c=1;c<=words;c++){

        printf("Type your word %d, after that press enter, then press 'control' and 'z' at the same time, and then press enter again: \n", c);

        while (EOF != (text=getchar())){

            if (text == 'a' || text == 'A'){    
                a++;
                if (a >=2){
                    a = 1;
                }
            }

            if (text == 'e' || text == 'E'){
                e++;
                if (e >=2){
                    e = 1;
                }
            }

            if (text == 'i' || text == 'I'){
                i++;
                if (i >=2){
                    i = 1;
                }
            }

            if (text == 'o' || text == 'O'){
                o++;
                if (o >=2){
                    o = 1;
                }
            }

            if (text == 'u' || text == 'U'){
                u++;
                if (u >=2){
                    u = 1;
                }
            }
        }

        vowels = a+e+i+o+u;

        if(vowels >=3){
            total = total +1;  
        }

        a=0,e=0,i=0,o=0,u=0;
        vowels = 0;   
    }

    printf("\n\nThe total of words with 3 or more vowels is: %d", total);
    printf("\n");

    total=0;

    return 0;
}

Upvotes: 0

Views: 347

Answers (2)

David C. Rankin
David C. Rankin

Reputation: 84579

In order to read and analyze a single word, or a paragraph words to determine the number of words that contain at least three different vowels (of any case), this is one of the rare times when reading input with scanf (using the '%s' format specifier) actually is a reasonable choice.

Recall the '%s' format specifier will read characters up to the first whitespace. That gives you a simple way to read a word at a time from stdin. To end input, the user simply need to generate an EOF by entering ctrl+d (or ctrl+z on windows). This satisfies your paragraph requirement.

For parsing, you can take advantage of converting each character to lower case to simplify checking for vowels. Using a frequency array of 5 elements provides a simple way to track the number of different vowels found in each word. Then a final test to see if the number of vowels found equals the required number is all you need before incrementing your total word count for words with three different vowels.

A simple implementation would be something similar to:

#include <stdio.h>

enum { NREQD = 3, NVOWEL = 5, MAXC = 128 };    /* declare constants */

int main (void) {

    char word[MAXC] = "";   /* word buffer */
    size_t wordcnt = 0;     /* words with 3 different vowels */

    printf ("enter a word(s) below, [ctrl+d on blank line to end]\n");
    for (;;) {
        int vowels[NVOWEL] = {0},   /* frequency array */
            vowelcnt = 0,           /* vowels per-word */
            rtn;                    /* scanf return */
        if ((rtn = scanf ("%127s", word)) == EOF)   /* chk EOF */
            break;
        for (int i = 0; word[i]; i++) { /* loop over each char */
            if ('A' <= word[i] && word[i] <= 'Z')   /* check upper */
                word[i] ^= 'a' - 'A';   /* convert to lower */
            switch (word[i]) {          /* check if vowel */
                case 'a':   vowels[0] = 1; break;
                case 'e':   vowels[1] = 1; break;
                case 'i':   vowels[2] = 1; break;
                case 'o':   vowels[3] = 1; break;
                case 'u':   vowels[4] = 1; break;
            }
        }
        for (int i = 0; i < NVOWEL; i++)    /* loop over array */
            if (vowels[i])                  /* check index */
                vowelcnt++;                 /* increment vowelcnt */
        if (vowelcnt >= NREQD)  /* do we have at least 3 vowels? */
            wordcnt++;          /* increment wordcnt */
    }

    printf ("\nThere are %zu words with %d different vowels.\n",
            wordcnt, NREQD);
}

Example Use/Output

$ ./bin/vowelcnt
enter a word(s) below, [ctrl+d on blank line to end]
Everyone Understands That The Dictionary Doesn't Track
Words That Contain Vowels Like It Does Etimology.

There are 4 words with 3 different vowels.

Look things over and let me know if you have further questions.

Upvotes: 2

Pablo
Pablo

Reputation: 13580

You can use fgets to read a whole line. I don't know how you define a paragraph though, do you mean just a long text or a collection of lines? You can copy & paste multiple lines in the console and if you loop using fgets, then you get all the lines. But allowing the user to enter multiple lines at once, it's more tricky, because you should know how many lines the user will input. That's why I'd say focus on reading the text line by line.

Your solution reads characters by characters and you are ignoring non-vowels. That's OK, but you are not detecting words like you should do. The for loop makes no sense, because in the first iteration you enter in a while loop that is only going to leave when there are no more characters to read from stdin. So the next iteration of the for loop will not enter the while loop and you won't be reading anything any more.

You are also repeating too much code, I know you assignment says not to use your own functions, but this can be improved with a simple look up table by creating an array of chars using the characters as an index for the array. I'll explain that in the code.

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

int main(void)
{
    char line[1024];

    // initializing look ups with 0
    int lookup_vowels[1 << CHAR_BIT] = { 0 };

    // using 'a', 'e' as index for the lookup table
    // if you want to know if a character is a vowel,
    // lookup_vowels[character] will be 1 if character is
    // a vowel, 0 otherwise
    lookup_vowels['a'] = lookup_vowels['e'] = lookup_vowels['i'] = 
            lookup_vowels['o'] = lookup_vowels['u'] = 1;

    // for parsing word with strtok
    const char *delim = " \t\r\n";

    int num_of_words = 0;

    printf("Enter some text, to end input press ENTER and then CTRL+D\n");

    while(1)
    {
        if(fgets(line, sizeof line, stdin) == NULL)
            break;

        // parsing words
        char *word = strtok(line, delim);
        if(word == NULL)
            continue; // the line has only delimiters, ignore it

        do {
            // will be access with the same principle as the lookup
            // table, the character is the index
            int present[1 << CHAR_BIT] = { 0 };

            size_t len = strlen(word);

            for(size_t i = 0; i < len; ++i)
            {
                // I'll explain later the meaning
                int c = tolower(word[i]);
                if(lookup_vowels[c])
                    present[c] = 1; // set the present for a vowel to 1
            }


            int count = present['a'] + present['e'] + present['i'] + present['o']
                + present['u'];

            if(count > 2)
            {
                printf("'%s' has more than three distinct vowels\n", word);
                num_of_words++;
            }

        } while((word = strtok(NULL, delim)));
    }

    printf("The number of word with three or more distinct vowels: %d\n", num_of_words);

    return 0;
}

So let me quickly explain some of the technique I use here:

The lookup table is an array of size 256 because a char is 8-bit1 value and can have 256 different values (range [0,255]). The idea is that this array is initialized with 0 overall (int lookup_vowels[1<<CHAR_BIT] = { 0 };) and then I set to 1 only in 5 places: at the position of the vowels using their ASCII value as index.

So instead of doing the repeating task if checking

// where c is a char
if(c == 'a' || c == 'A')
    a=1;
}

for all vowels, I just can do

int idx = tolower(c);
if(lookup_vowels[idx])
{
    // c is a vowel
}

The present variable function similar to the lookup table, here I use the ASCII code of a vowel as index and set it to 1 if a vowel is present in word. After scanning all characters in word, I sum all values stored in present. If the value is greater than 2, then the word has at least 3 or more distinct vowels and the counter variable is increased.

The function strtok is used to split the line using a defined set of delimiters, in this case the empty character, tab, carriage return and line feed. To start parsing the line, strtok must be called with the source string as the first argument and the delimiters as the second argument. All other subsequent calls must pass NULL as the first argument. The function returns a pointer to the next word and returns NULL when no more words have been found.

When a word is found, it calculates the number of distinct vowels and checks if this number is greater than 2.


fotenotes

1CHAR_BIT defined in limits.h returns the number of bits of byte. Usually a byte is 8-bit wide, so I could have written 256 instead. But there are "exotic" architectures where a byte is not 8-bit long, so by doing 1<<CHAR_BIT I'm getting the correct dimension.

Upvotes: 1

Related Questions