Exercise 1-14. Write a program to print a histogram of the frequencies of different characters in its input.
This Exercise is much like the exercise before. Let us solve it step by step.
First of course we need an array to store the occurrence number of every character. If you have ever known the ACII codes, it is easy to see that at this time we can store the occurrnece nubers in the array with length of 128. So lets do this in the main() function.
/*
---------------Before Main Function----------------
*/
#define ASCII_NUM 128
/*
---------------In Main Function--------------------
*/
int i, c, maxLen1, maxLen2;
int character[ASCII_NUM], character2[ASCII_NUM];
for (i = 0; i < ASCII_NUM; i++)
character[i] = 0;
while ((c = getchar()) != EOF) {
character[c]++;
}
for (i = 0; i < ASCII_NUM; i++)
character2[i] = character[I];
/*
More code ...
*/
We will see later why we need two arrays. And now let's constrate on how to draw the histogram, vertical version.
There is an obvious problem is that if you try to draw all elemets using one graph, the output window may fold your result, depending on your screen size. First fold the graph of bar and then the axis, which will be hard to read. It is a good idea to divide the histogram into many parts. For me, 2 part will suit my screen. First part is from NULL to character 'Z'. Second part is from character '[' to del.
To draw the first part, we should frist know how many lines we have to output, which is also the hight of histogram.
//Part1
maxLen1 = 0;
for (i = 0; i <= 'Z'; i++)
if (maxLen1 < character[i])
maxLen1 = character[i];
Then we can darw the graph row by row. And use '*' to from every bar. When we draw the n-th row, we need to walk through the character array and check whether we should put a '*' at this corresponding place: If the line we draw is highter than some value in array - e.g. : n > charachter['A'], than we should not draw a '*' at this place because the bar of 'A' is lower than this hight. In the other case we put a '*'.
while (maxLen1) {
for (i = 1; i < 'Z'; i++) {
if (character[i] > 0 ) {
if (character[i] >= maxLen1){
printf("* ");// add a space here for easier understanding
character[i]--;
} else {
printf(" ");
}
}
}
putchar('\n');
maxLen1--;
}
Finally we should add a axis for the graph. Because we have already used character[] for graph printing, the value in character[] are all 0. So we use the substitute array character2[] here.
// print character
for (i = 0; i <= 'Z'; i++) {
if (character2[i] > 0){
if (i == ' ')
printf("\\b ");
else if (i == '\t')
printf("\\t ");
else if (i == '\n')
printf("\\n ");
else
printf("%c ", i);
}
}
putchar('\n');
Now we have finished the first part of histogram. For second part, use the same pattern. And finally we can see the whole program as below:
#include <stdio.h>
#define ASCII_NUM 128
int main() {
int i, c, maxLen1, maxLen2;
int character[ASCII_NUM], character2[ASCII_NUM];
for (i = 0; i < ASCII_NUM; i++)
character[i] = 0;
while ((c = getchar()) != EOF) {
character[c]++;
}
for (i = 0; i < ASCII_NUM; i++)
character2[i] = character[i];
maxLen1 = 0;
for (i = 0; i <= 'Z'; i++)
if (maxLen1 < character[i])
maxLen1 = character[i];
while (maxLen1) {
for (i = 1; i < 'Z'; i++) {
if (character[i] > 0 ) {
if (character[i] >= maxLen1){
printf("* ");
character[i]--;
} else {
printf(" ");
}
}
}
putchar('\n');
maxLen1--;
}
for (i = 0; i <= 'Z'; i++) {
if (character2[i] > 0){
if (i == ' ')
printf("\\b ");
else if (i == '\t')
printf("\\t ");
else if (i == '\n')
printf("\\n ");
else
printf("%c ", i);
}
}
putchar('\n');
maxLen2 = 0;
for (i = 'Z' + 1; i < ASCII_NUM; i++)
if (maxLen2 < character[i])
maxLen2 = character[i];
while (maxLen2) {
for (i = 'Z' + 1; i < ASCII_NUM; i++) {
if (character[i] > 0 ) {
if (character[i] >= maxLen2){
printf("* ");
character[i]--;
} else {
printf(" ");
}
}
}
putchar('\n');
maxLen2--;
}
for (i = 'Z' + 1; i < ASCII_NUM; i++) {
if (character2[i] > 0){
if (i == ' ')
printf("\\b ");
else if (i == '\t')
printf("\\t ");
else if (i == '\n')
printf("\\n ");
else
printf("%c ", i);
}
}
putchar('\n');
return 0;
}
And you can see output like this:
> ./Exercise1_14 < test.txt
*
* *
* * * *
* * * * * * *
\n , . / 1 2 3
*
*
* *
* * * *
* * * * *
a b d f s
End.