Один из наиболее простых c подходов, когда вам нужно подсчитать, сколько раз любой элемент в коллекции встречается для данного набора, - это использовать Frequency Array . Очень просто, Frequency Array содержит количество элементов, которые могут встречаться в коллекции. (например, с какой частотой встречается объект, сопоставленный с каждым индексом?)
Например, если вы хотите знать, сколько раз какой-либо альфа-символ встречается в строке текста (без учета регистра), ваш В коллекции 26 элементов (буквы алфавита). Таким образом, вы просто объявляете массив из 26 элементов (обычно int
или size_t
), инициализируете все нулями , а затем l oop над символами в вашей строке текста и для каждого альфа-символ, вы увеличиваете соответствующий элемент массива.
Например, с массивом int chars[26] = {0};
и для строки, содержащей "Dog"
, вы просто увеличиваете:
chars[toupper(line[0]) - 'A']++; /* increments index chars[3]++ */
chars[toupper(line[1]) - 'A']++; /* increments index chars[14]++ */
chars[toupper(line[2]) - 'A']++; /* increments index chars[6]++ */
(см. Таблицу ASCII и описание , чтобы понять, как toupper(line[0]) - 'A'
сопоставляет символ с соответствующим индексом. Также обратите внимание, что приведение к unsigned char
намеренно опущено для ясности)
После цикла по всем символам в строке вы можете вывести результаты, перебирая все элементы массива частот, и если значение в индексе больше нуля, вы выводите символ, соответствующий этому индексу, и значение, удерживаемое как счетчик, например
for (int i = 0; i < 26; i++)
if (chars[i])
printf ("[%c] => %d\n", i + 'A', chars[i]);
Чтобы обработать символы di git в строке, вы просто используете второй 10-элементный частотный массив и делаете то же самое, но на этот раз subt racting '0'
для сопоставления цифр ASCII с элементами 0-9
массива.
Другой аспект проблемы, который вы пропустили, - это проверка каждого ввода и преобразования. Вы не можете правильно использовать какую-либо функцию ввода, если не проверит возврат , чтобы определить, успешно ли завершилась операция. То же самое относится к каждому преобразованию в тип numeri c и к любому другому выражению, критически важному для продолжения определенной операции вашего кода.
Собирая части вместе, вы можете использовать глобальный enum
для определения необходимых констант. для вашей программы (или к тому же с несколькими операторами #define
) и откройте файл, прочтите и преобразуйте первую строку в целочисленное значение с помощью:
#include <stdio.h>
#include <ctype.h>
enum { DIGITS=10, CHARS=26, MAXC=1024 }; /* constants for use in program */
int main (int argc, char **argv) {
char line[MAXC]; /* buffer to hold each line of input */
int ncases = 0; /* integer for number of cases */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (!fgets (line, MAXC, fp)) { /* read/validate 1st line */
fputs ("(user canceled input)\n", stdout);
return 0;
}
if (sscanf (line, "%d", &ncases) != 1) { /* convert/validate to int */
fputs ("error: invalid integer input for ncases.\n", stderr);
return 1;
}
Теперь с количеством случаев в ncases
, вы просто l oop это количество раз, считывая следующую строку из файла, перебирая каждый символ в line
, увеличивая соответствующие элементы двух частотных массивов chars
и digits
для подсчета вхождений каждого типа символа (alpha
и digits
), а затем вывести результаты, последовательно просматривая каждый частотный массив, чтобы вывести счетчики из каждого. Вы можете сделать это следующим образом:
for (int i = 0; i < ncases; i++) { /* loop ncases times */
int chars[CHARS] = {0}, /* frequency array for characters */
digits[DIGITS] = {0}; /* frequency array for digits */
if (!fgets(line, MAXC, fp)) { /* read/validate line */
fputs ("error: reading line from file.\n", stderr);
return 1;
}
for (int j = 0; line[j]; j++) { /* loop over each char in line */
int c = toupper((unsigned char)line[j]); /* convert to uppercase */
if (isalpha((unsigned char)c)) /* check if A-Z */
chars[c-'A']++; /* increment chars at index */
else if (isdigit((unsigned char)c)) /* check if 0-9 */
digits[c-'0']++; /* increment digits at index */
}
printf ("\nCase #%d:\n\n", i + 1); /* output case no. */
for (int j = 0; j < CHARS; j++) /* loop over chars array */
if (chars[j]) /* if value at index non-zero */
printf ("[%c] => %d\n", j + 'A', chars[j]); /* output count */
for (int j = 0; j < DIGITS; j++) /* loop over digits array */
if (digits[j]) /* if value at index non-zero */
printf ("[%d] => %d\n", j, digits[j]); /* output count */
}
( примечание: для вывода количества цифр, нет необходимости сопоставлять индексы с их значением ASCII - вы можете просто вывести целочисленное представление с %d
вместо сопоставления j + '0'
для вывода символов ASCII с %c
- действительно ваш выбор. Также см. man 3 isalpha для требования, чтобы каждый аргумент любой из классификации макросы должны быть типа unsigned char
- объясняя цель приведенных выше приведений к (unsigned char)
)
Это, по сути, полный код для решения вашей проблемы с частотными массивами. Все, что вам нужно, это привести в порядок и вернуть успех оболочке, например
if (fp != stdin) /* close file if not stdin */
fclose (fp);
return 0;
}
Пример использования / вывода
Размещение образца ввода в файл dat/charcountlines.txt
а затем предоставление этого в качестве входных данных для программы приведет к следующему:
$ ./bin/charcount dat/charcountlines.txt
Case #1:
[A] => 4
[B] => 1
[E] => 1
[I] => 3
[N] => 4
[R] => 2
[S] => 2
[T] => 2
[U] => 2
[V] => 1
[Y] => 1
Case #2:
[B] => 1
[I] => 1
[N] => 1
[S] => 1
[U] => 1
[0] => 2
[2] => 2
Case #3:
[A] => 1
[C] => 2
[D] => 1
[I] => 2
[N] => 1
[O] => 3
[R] => 2
[S] => 1
[U] => 1
[V] => 2
[1] => 1
[9] => 1
Приведенное выше соответствует указанному вами выводу и порядку. Просмотрите все и дайте мне знать, если у вас возникнут дополнительные вопросы.