-3

I need to covert this code from a user input using gets() to scanning in an input txt file. It would also be helpful to find a way to count the number of letters in every word. Starting with 1 letter words and on.

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


int main(void) {

    int acount, bcount, ccount, dcount, ecount, fcount, gcount, hcount, icount, jcount, kcount, lcount, mcount, ncount, ocount, pcount, qcount, rcount, scount, tcount, ucount, vcount, wcount, xcount, ycount, zcount = 0;
    char *str;

    printf("Enter any string : ");
    gets(str);

    while (*str != '\0')
    {
        if(isalpha(*str))
           {
               toupper(*str);
               switch(*str)
               {
                       case 'A':
                       ++acount;
                       break;

                       case 'B':
                       ++bcount;
                       break;

                       case 'C':
                       ++ccount;
                       break;

                       case 'D':
                       ++dcount;
                       break;

                       case 'E':
                       ++ecount;
                       break;

                       case 'F':
                       ++fcount;
                       break;

                       case 'G':
                       ++gcount;
                       break;

                       case 'H':
                       ++hcount;
                       break;

                       case 'I':
                       ++icount;
                       break;

                       case 'J':
                       ++jcount;
                       break;

                       case 'K':
                       ++kcount;
                       break;

                       case 'L':
                       ++lcount;
                       break;

                       case 'M':
                       ++mcount;
                       break;

                       case 'N':
                       ++ncount;
                       break;

                       case 'O':
                       ++ocount;
                       break;

                       case 'P':
                       ++pcount;
                       break;

                       case 'Q':
                       ++qcount;
                       break;

                       case 'R':
                       ++rcount;
                       break;

                       case 'S':
                       ++scount;
                       break;

                       case 'T':
                       ++tcount;
                       break;

                       case 'U':
                       ++ucount;
                       break;

                       case 'V':
                       ++vcount;
                       break;

                       case 'W':
                       ++wcount;
                       break;

                       case 'X':
                       ++xcount;
                       break;

                       case 'Y':
                       ++ycount;
                       break;

                       case 'Z':
                       ++zcount;
                       break;

               }//Close case
           }//Close if
    }//Close while

    printf("Number of A's: %d", acount);

}

2 Answers2

3

Many errors in your code

  1. You don't initialize all the (x)count variables to 0, read about the coma operator.

  2. You used gets() which is a mean and deprecated function.

  3. You passed an uninitialized pointer to gets() which is undefined behavior.

  4. toupper(*str) does not modify *str.

Try this one

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

int main(void) 
{
    /* You need an array of int's with size equal to the number of letters
     * int the alphabet
     */
    int count['Z' - 'A' + 1];
    /* You need some space to store the text, `str' will become a poitner
     * when you pass it to `fgets()' pointing to an array of 1000 `chars'
     */
    char str[1000];
    /* Initialize all the counters to 0 */
    for (int i = 0 ; i < sizeof(count) / sizeof(*count) ; ++i)
        count[i] = 0;
    printf("Enter any string : ");
    /* Read the string, use `fgets()` and prevent a buffer overflow */
    if (fgets(str, sizeof(str), stdin) == NULL)
        return -1;
    /* Now count the letters */
    for (int i = 0 ; ((str[i] != '\0') && (str[i] != '\n')) ; ++i)
    {
        /* If it's not a letter, go to the next one */
        if (isalpha((int) str[i]) == 0)
            continue;
        /* It's a letter, count it at the correct position */
        count[toupper((int) str[i]) - 'A'] += 1;
    }

    /* Print the count of each letter, skipping those that did not appear */
    for (int i = 0 ; i < sizeof(count) / sizeof(*count) ; ++i)
    {
        if (count[i] == 0)
            continue;
        fprintf(stderr, "Number of %c's : %d\n", i + 'A', count[i]);
    }
    return 0;
}
Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
  • 1
    Wow you're fast. You might want to note that gets is deprecated because it basically guarantees that a user can cause a buffer overflow in your program. – Bobby Sacamano Nov 15 '15 at 21:07
  • `((str[i] != '\n') && (str[i] != '\n'))` is redundant and will cause UB if the last line of the file does not end with a linefeed. Just use `str[i] != '\0'` as you only count letters anyway. `isalpha(str[i])` should be `isalpha((unsigned char)str[i])` to account for stupid systems where `char` is signed. For the same reason, you should write `count[toupper((unsigned char)str[i]) - 'A'] += 1;`. Also be aware that `isalpha` may be true for non-ASCII characters and `toupper` can return values greater than `'Z'` in some locales. – chqrlie Nov 15 '15 at 21:31
  • @chqrlie Of course it is! And `isalpha()` expects `int` not `unsigned char`. – Iharob Al Asimi Nov 15 '15 at 22:11
  • 1
    I'm afraid you misunderstood my remark about `char` signedness. You need to cast `char` argument to the functions from `` in order to prevent sign extension of negative characters that will invoke undefined behaviour. They indeed take an `int` argument, but are only defined for all values of `unsigned char` and the value `EOF`. See C11, 7.4 paragraph 1. You should not call `isalpha` with a `char` argument if the type `char` is signed. The simple fix is to cast the `char` value to `unsigned char`, the `unsigned char` value will then be automatically converted to `int`. – chqrlie Nov 15 '15 at 22:18
  • I think this is non-portable `int count['Z' - 'A' + 1];`. In case the execution character set has the alphabet backwards or something like that, this won't work. I'm not sure if there is a portable thing that will do what you're attempting. – Bobby Sacamano Nov 15 '15 at 23:14
  • How do I edit this to read in a file? – Blake Doran Nov 16 '15 at 02:55
  • @BlakeDoran Use [`fopen()`](http://man7.org/linux/man-pages/man3/fopen.3.html) and pass the handler to `fgets()`. – Iharob Al Asimi Nov 16 '15 at 03:59
  • Note: In C11, `gets()` is removed. (passed depreciation) – chux - Reinstate Monica Nov 16 '15 at 21:46
0

All remarks in iharob response are correct. Read my comment on his response for extra problems. Here is a simple solution:

#include <stdio.h>

int main(void) {
    int counts[256] = { 0 };
    int c;

    printf("Enter any string: ");
    while ((c = getchar()) != EOF && c != '\n') {
        counts[c & 255]++;
    }
    printf("Number of A's: %d", counts['a'] + counts['A']);
    return 0;
}

EDIT: here is a solution to handle a file instead of a line of input:

#include <stdio.h>

int main(int argc, char **argv) {
    long long int counts[256] = { 0 };
    int c;
    FILE *f;

    if (argc < 2) {
        fprintf(stderr, "usage: %s FILE\n", argv[0]);
        exit(2);
    }
    if ((f = fopen(argv[1], "r")) == NULL) {
        fprintf(stderr, "%s: cannot open %s\n", argv[0], argv[1]);
        exit(1);
    }
    while ((c = getc(f)) != EOF) {
        counts[c & 255]++;
    }
    fclose(f);
    printf("Number of A's: %lld", counts['a'] + counts['A']);
    return 0;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • How can edit this program so it scans a file for the text rather than user input? – Blake Doran Nov 15 '15 at 22:24
  • You can simply remove the test for `'\n'` and use command line redirection to read you file via standard input: `myprogram < myfile.txt`. You might want to use a larger type than `int` for pathological cases where the file has more than 2 billion `'a'`s in it. – chqrlie Nov 15 '15 at 22:29
  • Could you explain more? I am confused – Blake Doran Nov 16 '15 at 03:20
  • @Blake Doran: I edited the answer with extra code to handle a file of any length. The filename is passed as a command line argument. – chqrlie Nov 16 '15 at 21:35