Поиск и подсчет символов верхнего и нижнего регистров
Я написал программу на языке Си, работающую на UNIX, которая подсчитывает количество каждой буквы во входном текстовом файле. Для такого файла:
"кошка сидела на зеленой циновке"
Вывод будет выглядеть следующим образом:
The letter ’a’ occurs 3 times.
The letter ’c’ occurs 1 times.
The letter ’e’ occurs 4 times.
The letter ’g’ occurs 1 times.
The letter ’h’ occurs 2 times.
The letter ’m’ occurs 1 times.
The letter ’n’ occurs 2 times.
The letter ’o’ occurs 1 times.
The letter ’r’ occurs 1 times.
The letter ’s’ occurs 1 times.
The letter ’t’ occurs 5 times.
5 *
4 * *
4 * *
3 * * *
3 * * *
2 * * * * *
2 * * * * *
1 * * * ** *** ***
1 * * * ** *** ***
0 **************************
0 **************************
... abcdefghijklmnopqrstuvwxyz
Где график представляет количество раз, когда появляется буква. (Если это больше 10, я просто ставлю " + " после 10-го ряда). Код, который я в настоящее время написал для достижения этой цели, выглядит следующим образом:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
void drawGraph(int letters[26], char alpha[26]);
void printLetters(int letters[26], char alpha[26]);
void getLetters(FILE *fp, int letters[26], char alpha[26]);
int main(int argc, char *argv[]) {
FILE *fp;
int letters[26] = { 0 };
char alpha[26] = { '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' };
int indexedAlpha[256] = { 0 };
int j = 1;
for (i = 97; i <= 127; i++)
{
indexedAlpha[i] = j;
j++;
}
//open file
if ((fp = fopen(argv[1], "r")) == NULL)
{
perror("Cannot open file");
exit(EXIT_FAILURE);
}
getLetters(fp, letters, alpha);
printLetters(letters, alpha);
printf("n");
drawGraph(letters, alpha);
printf("n");
return EXIT_SUCCESS;
}
void getLetters(FILE *fp, int letters[26], char alpha[26]) {
int c;
for (int i = 0; (c = fgetc(fp)) != EOF; i++)
{
c = fgetc(fp);
if ( isalpha(c) )
{
for ( int j = 0; j < 26; j++ ) //find which letter it is
{
if( c == alpha[j] )
{
letters[j]++;
break;
}
}
}
}
}
void printLetters(int letters[26], char alpha[26]) {
for( int i = 0; i < 26; i++ )
{
if(letters[i] != 0){
printf("The letter '%c' occurs %d times.n", alpha[i], letters[i]);
}
}
}
void drawGraph(int letters[26], char alpha[26]) {
int x = 11;
int y;
while(x >= 0)
{
y = 0;
while (y < 2)
{
if (x == 10)
{
printf(" %d ", x);
}
else if (x == 11)
{
printf(" ");
}
else
{
printf(" %d ", x);
}
for( int i = 0; i < 26; i++ )
{
if(letters[i] > 10)
{
printf("+");
letters[i] = 10;
y++; // Break out of while loop
}
else if(letters[i] == x)
{
printf("*");
}
else
{
printf(" ");
}
if (letters[i] == x && y == 1)
{
letters[i] = letters[i] - 1;
}
}
printf("n");
y++;
}
x--;
}
printf("... ");
for( int i = 0; i < 26; i++ )
{
printf("%c", alpha[i]);
}
}
Однако мой текущий код имеет две проблемы. 1. Я всегда распечатывайте 10 точек оси Y я хочу распечатать только столько точек оси Y, сколько необходимо, каков был бы лучший способ достичь этого? 2. В настоящее время учитываются только строчные символы, как я могу решить эту проблему?
Также любая нотация или лучшая методология были бы очень оценены, я все еще пытаюсь учиться!
Спасибо!
2 ответа:
2-й вопрос кажется мне немного легким:
2. Currently only lower case characters are counted, how can i address this?
Составьте такую структуру:
Таким образом, вы можете отслеживать каждую букву с ее количеством.typedef struct { char c; int count; }alpha; alpha abc[26]; for(i=0 ; i<26 ; i++) abc[i].count = 0; // Initialization of count for each alphabet for(i=0;i<26; i++) abc[i].c = 'a' + i;
Для печати "гистограммы" каждого алфавита в файле вам нужны
(11 + 1 + 1)
строки(означает не менее 13 строк, могут быть дополнительные строки для разделения символов и их бара)
Но чтобы напечатать эти строки, вам нужно позаботиться о дополнительных вещах о пространстве перед каждым из
11
строки для11
времен появления алфавита и1
для+
, Как уже упоминалось, и1
для алфавитов сам.*
(символ гистограммы, который вы использовали).Итак, пройдите через массив и распечатайте его, например, для вашего
1st question
попробуйте что-то вроде этого:int cnt; for(cnt = 11 ; c >=0 ; c--) { for(i=0; i<26; i++) { if(abc[i].count >= cnt && cnt == 11) { space = abc[i].c - 'a'; printf("%*c",space,'+'); // setting indentation and printing } if(abc[i].count == cnt && cnt != 11) { space = abc[i].c - 'a'; printf("%*c",space,'*'); //// setting indentation and printing } printf("\n"); } //end of inner for loop } // end of outer for loop printf("abcdefghijklmnopqrstuvwxyz\n");
Другое представление данных упростило бы вашу программу.
Используйте одну таблицу для хранения всех возможных однобайтовых символов; байт val символа char служит ключом; # of occurences - val.
Обратите внимание на использование
Программа ниже обрабатывает как прописные, так и строчные буквы, но ограничивается однобайтовыми кодировками-она не обрабатывает должным образом utf8, utf16 и т. д. Для получения дополнительной информации о кодировках текста прочитайте эту статью . То, что isalpha() трактует как букву, зависит от текущей локали, которая может быть изменена с помощью setlocalewhile ((c = getchar()) != EOF)
; идиома для чтения ввода по одному байту за раз-ваша процедура getLetters() пропускает все остальные символы.#include <stdio.h> #include <ctype.h> #include <limits.h> enum { NROWS = 10 }; void draw_line(int count[], double scale, int level); void draw(int count[]); int main() { int count[UCHAR_MAX+1] = {0}; int c; while ((c = getchar()) != EOF) count[c]++; for (c = 0; c <= UCHAR_MAX; c++) if (isalpha(c) && count[c] != 0) printf("%c %d\n", c, count[c]); draw(count); return 0; }
Моя версия графика draw, масштабирует значения y вместо печати " + " для больших значений. Он распечатывает только как можно больше "y-ось звезды" как необходимости осуществляются путем сравнения граф до уровня y-оси.
void draw(int count[]) { int c, i; int max = 0; double scale; for (c = 0; c <= UCHAR_MAX; c++) if (isalpha(c) && count[c] > max) max = count[c]; scale = (max == 0) : 1.0 : (double)max / NROWS; for (i = NROWS; i > 0; i--) draw_line(count, scale, i); for (c = 0; c <= UCHAR_MAX; c++) if (isalpha(c)) putchar(c); putchar('\n'); } void draw_line(int count[], double scale, int level) { int c; for (c = 0; c <= UCHAR_MAX; c++) { if (isalpha(c) && count[c] / scale >= level) putchar('*'); else if (isalpha(c)) putchar(' '); } putchar('\n'); }
Пример вывода графика:
$ ./countchars < countchars.c [..snip..] * * * * * * * * * * * * * * * * * * * * * * * * * * ** * ** * *** ** * ** * ** * * **** ** * *** **** * * * * * * * * **** ** ***** ****** ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz