Поиск и подсчет символов верхнего и нижнего регистров


Я написал программу на языке Си, работающую на 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   3  

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.

Обратите внимание на использование while ((c = getchar()) != EOF); идиома для чтения ввода по одному байту за раз-ваша процедура getLetters() пропускает все остальные символы.

Программа ниже обрабатывает как прописные, так и строчные буквы, но ограничивается однобайтовыми кодировками-она не обрабатывает должным образом utf8, utf16 и т. д. Для получения дополнительной информации о кодировках текста прочитайте эту статью . То, что isalpha() трактует как букву, зависит от текущей локали, которая может быть изменена с помощью setlocale
#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