Search code examples
carrayscountcharacterlogarithm

Count alphabets in C using log functions(without math.h) and arrays


I'm facing a slight problem with one of my projects. I am supposed to write a c program to calculate each character present in the input/file. (It's supposed to be a basic program.) The constraints - I cannot use the math.h library to produce log functions and obtain an output in the format:

    1                                           
    5   1 2 0 2 2 5 8     4 3 6 6 2   5 5 7 2 1 1   2   
    7 9 8 1 7 2 4 1 0 0 4 5 0 2 2 5 2 6 3 6 6 3 7 0 2 2 
    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

The program is supposed to count the number of occurrences of each alphabetic letter (case insensitive) in the stdin input stream and display a histogram.

As you can see, the output is formatted vertically with each line printing the base 10 number of the position of the character. Now, this might seem silly, but what I have so far is this:

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

    /*
    int logBase10 (int num) {
       method to calculate the log base 10 of num
    }
    */

    int main (int argc, char * argv[]) {
        char alpha;
        int count = 0;
        int ascii[128] = {0};

        while ( (alpha = getchar()) != EOF) {
            count++;
            ascii[(int)alpha]++;
            alpha = getchar();
        }

        printf("Char \t Count \n");
        printf("------------------------\n");

        for (int i = 0; i < 127; i++) {
            if(ascii[i] > 0) {
                printf("%c \t %d \n", i, ascii[i]);
            }
        }
    }

Which produces an output like this:

    Char     Count 
    ------------------------

         5 
         93 
    ,    6 
    -    2 
    .    3 
    ;    2 
    C    2 
    I    6 
    N    1 
    T    1 
    W    2 
    a    26 
    b    5 
    c    8 
    d    13 
    e    55 
    f    11 
    g    7 
    h    28 
    i    32 
    k    3 
    l    26 
    m    17 
    n    31 
    o    27 
    p    12 
    q    1 
    r    26 
    s    22 
    t    42 
    u    11 
    v    8 
    w    8 
    y    13 
    z    1 

First off, my program is printing unwanted ascii characters (, ; - etc) and I am working on changing the print function to be more vertical, but I cannot figure out the log method at all. I know log(10) is 1 because 10^1 is 1, but I am having trouble figuring out how to use this to create the method itself. Also, for the extra characters, I tried using:

    if(ascii[i] > 65 || ascii[i] < 90 || ascii[i] >= 97 || ascii[i] <= 122 ) {
        printf("%c \t %d \n", i, ascii[i]);
    }

to no avail. Trying that produced more gibberish characters instead.

Any help/feedback is appreciated.

  • Soul

Solution

  • The commenters have already pointed out issues with your code. Here's a version that counts only letters and prints vertical labels. It doesn't need <ctype.h> or <math.h>.

    • Each character hets a letter index which is a number from 0 to 25 for upper and lower case letters and −1 if the character isn't a letter. That reduces the array size to 26.

    • You could find out each digit with elaborate calculations, but the easiest way is to print the number to a string. snprintf does this for you. You can right-align the number with a field width. The maximum value for a typical int is about 2 billion, which has 10 digits. You should account for that, even if you had to pass in the whole Moby-Dick plus the Bible to get that many counts.

    • You can test whether you should start printing by assuming a width of ten digits first and checking whether the maximum count has ten digits, that is whether it is 1,000,000,000 or higher. Then divide that limit by 10 in each iteration.

    Here's the code:

    #include <stdio.h>
    
    // return letter index or -1 for non-letter
    int letter(int c)
    {
        if ('a' <= c && c <= 'z') return c - 'a';
        if ('A' <= c && c <= 'Z') return c - 'A';
        return -1;
    }
    
    int main(int argc, char * argv[])
    {
        int count[26] = {0};        // letter counts
        char label[26][12];         // buffer for printing numbers
        int limit = 1000000000;     // smallest 10-digit number
        int max = 0;
        int i, j;
    
        // read and count letters
        while (1) {
            int c = getchar();
    
            if (c == EOF) break;
    
            c = letter(c);
            if (c >= 0) count[c]++;
        }
    
        // write auxiliary labels
        for (i = 0; i < 26; i++) {
            snprintf(label[i], sizeof(label[i]), "%10d", count[i]);
            if (count[i] > max) max = count[i];
        }
    
        // print vertical labels
        for (j = 0; j < 10; j++) {
            if (max >= limit) {        
                for (i = 0; i < 26; i++) {
                    putchar(' ');
                    putchar(label[i][j]);
                }
                putchar('\n');
            }
            limit /= 10;
        }
    
        // print horizontal rule
        for (i = 0; i < 26; i++) {
            putchar('-');
            putchar('-');
        }
        putchar('-');
        putchar('\n');
    
        // print letters
        for (i = 0; i < 26; i++) {
            putchar(' ');
            putchar('A' + i);
        }
        putchar('\n');
    
        return 0;
    }
    

    On your example, it produces:

             1                                          
     5   1 2 0 2 2 5 8     4 3 6 6 2   5 5 7 2 1 1   2  
     7 9 8 1 7 2 4 1 0 0 4 5 0 2 2 5 2 6 3 6 6 3 7 0 2 2
    -----------------------------------------------------
     A B C D E F G H I J K L M N O P Q R S T U V W X Y Z