Хотя из вашего вопроса и последующих комментариев неясно, где именно вы застряли, или вы бросили полотенце и сдались, давайте рассмотрим его с самого начала.
ОткрытьВаш файл (или чтение stdin
)
Прежде чем вы сможете что-либо сделать с содержимым вашего файла, вам необходимо открыть файл для чтения.Для чтения форматированного ввода вы обычно будете использовать функции, которые читают и пишут из файлового потока, используя указатель потока FILE *
(в отличие от низкоуровневого file-descriptor file interface).Чтобы открыть файл, вам нужно позвонить fopen
и , чтобы проверить return , чтобы подтвердить успешное открытие.
Не вводите жесткий кодимена файлов или номера в вашей программе.Ваша программа принимает аргументы, либо передайте имя файла для открытия в качестве аргумента, либо запросите ввод имени файла.Вы можете повысить гибкость своей программы, взяв имя файла для чтения в качестве аргумента или прочитав из stdin
по умолчанию, если аргумент не предоставлен (как это делают большинство утилит Linux).Поскольку stdin
является файловым потоком, вы можете просто назначить его указателю FILE*
, если не открываете имя файла, указанное в качестве аргумента.Например:
FILE *fp = NULL;
if (argc > 1) /* if one argument provided */
fopen (argv[1], "r"); /* open file with name from argument */
else
fp = stdin; /* set fp to stdin */
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
, который можно сократить с помощью оператора троичный , например:
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
Чтение ваших данных
С открытым потоком файлов и подтверждением 1037 * теперь вы можете читать свои данные из файла.Хотя вы можете читать с fscanf
, вы ограничены в информации, которую он предоставляет в случае, если два значения не читаются.Кроме того, чтение с помощью семейства функций scanf
сопряжено с трудностями, связанными с тем, какие символы остаются во входном потоке файлов в зависимости от используемых спецификаторов преобразования и от того, было ли преобразование выполнено успешно или нет.Тем не менее, простой подход, который проверяет два преобразования, был выполнен в соответствии с вашей format-string , что позволит вам прочитать ваш файл, например,
char c1, c2; /* characters from each line */
int freq1[MAXC] = {0}, freq2[MAXC] = {0}; /* frequency arrays */
...
while (fscanf (fp, " %c, %c", &c1, &c2) == 2) /* read all chars */
if (c1 > 0 || c2 > 0) /* validate ASCII values */
/* increment element in each */
freq1[(unsigned char)c1]++, freq2[(unsigned char)c2]++;
(недостатком является любое изменение формата наодна строка может оставить вас с ненужными символами, и если произойдет менее двух преобразований, ваш цикл чтения прекратится, несмотря на то, что действительные данные остаются непрочитанными)
Лучшим подходом является чтение строки за раз с линейно-ориентированная функция ввода, такая как fgets
или POSIX getline
.При таком подходе вы одновременно используете строку данных, а затем анализируете необходимую информацию из сохраненной строки.Преимущества являются значительными.У вас есть независимая проверка самого чтения, а затем вы найдете нужные значения в строке.Если ваш формат отличается и вы анализируете меньше, чем необходимые значения из строки, у вас есть возможность просто пропустить эту строку и перейти к следующей.Кроме того, то, что остается в потоке входного файла, не зависит от используемых спецификаторов преобразования.
Пример с fgets
и sscanf
, делающими то же самое:
char c1, c2, /* characters from each line */
buf[MAXC] = ""; /* buffer to hold each line */
...
while (fgets (buf, MAXC, fp)) /* read all chars */
if (sscanf (buf, " %c, %c", &c1, &c2) == 2) { /* parse values */
if (c1 > 0 || c2 > 0) /* validate ASCII values */
/* increment element in each */
freq1[(unsigned char)c1]++, freq2[(unsigned char)c2]++;
}
else
fputs ("error: in line format.\n", stderr);
Обработка частоты символов
Если вы обращали внимание на чтение данных из файла, вы заметите, что пара частотных массивов увеличивалась при каждом чтениисимволы freq1
и freq2
.Как упоминалось в моих комментариях выше, вы начинаете с массива адекватного размера int
для хранения набора символов ASCII.Массивы инициализируются нулями.Когда вы читаете символ из каждого столбца, вы просто увеличиваете значение на:
if (c1 > 0 || c2 > 0) /* validate ASCII values */
/* increment each element */
freq1[(unsigned char)c1]++, freq2[(unsigned char)c2]++;
Например, значение ASCII для 'a'
равно 97
(см. Таблица ASCII и описание ).Поэтому, если вы читаете 'a'
и увеличиваете
freq1['a']++;
, что аналогично увеличению:
freq1[97]++;
Когда вы закончите цикл чтения, вам просто нужно будет выполнить итерациюваши частотные массивы от 'a'
до 'z'
и количество раз, когда соответствующий символ, появившийся в вашем файле, будет записан в ваш массив.Тогда вы можете использовать данные по своему усмотрению.
Вывод результатов
Самый простой способ вывести результаты column1 / column2 - просто вывести количество вхождений для каждого символа.Например:
for (int i = 'a'; i <= 'z'; i++) /* loop over 'a' to 'z' */
printf (" %c: %d, %d\n", i, freq1[i], freq2[i]);
, который будет производить вывод, похожий на:
$ ./bin/freq_dual_col2 <dat/char2col.txt
lowercase occurrence:
a: 1, 1
b: 1, 0
c: 1, 1
d: 1, 0
e: 1, 1
f: 1, 0
...
Если вы хотите получить более подробный текст и обратите внимание, появляются ли символы "none"
или 1
или если персонаж был дублирован "dupe"
, вы можете использовать несколько дополнительных проверок, например,
for (int i = 'a'; i <= 'z'; i++) { /* loop over 'a' to 'z' */
if (freq1[i] == 1) /* check col 1 chars */
printf (" %c , ", i);
else if (!freq1[i])
fputs ("none, ", stdout);
else
fputs ("dupe, ", stdout);
if (freq2[i] == 1) /* check col 2 chars */
printf (" %c\n", i);
else if (!freq2[i])
fputs ("none\n", stdout);
else
fputs ("dupe\n", stdout);
}
, которые будут производить вывод в виде:
$ ./bin/freq_single_dual_col <dat/char2col.txt
lowercase single occurrence, none or dupe:
a , a
b , none
c , c
d , none
e , e
f , none
...
В целом, ваш минимальныйПример использования fscanf
для чтения может быть похож на:
#include <stdio.h>
#include <limits.h>
#define MAXC UCHAR_MAX+1
int main (int argc, char **argv) {
char c1, c2; /* characters from each line */
int freq1[MAXC] = {0}, freq2[MAXC] = {0}; /* frequency arrays */
/* 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;
}
while (fscanf (fp, " %c,%c", &c1, &c2) == 2) /* read all chars */
if (c1 > 0 || c2 > 0) /* validate ASCII values */
/* increment each element */
freq1[(unsigned char)c1]++, freq2[(unsigned char)c2]++;
if (fp != stdin) fclose (fp); /* close file if not stdin */
puts ("lowercase occurrence:\n");
for (int i = 'a'; i <= 'z'; i++) /* loop over 'a' to 'z' */
printf (" %c: %d, %d\n", i, freq1[i], freq2[i]);
return 0;
}
Пример использования fgets
и sscanf
будет похож на:
#include <stdio.h>
#include <limits.h>
#define MAXC UCHAR_MAX+1
int main (int argc, char **argv) {
char c1, c2, /* characters from each line */
buf[MAXC] = ""; /* buffer to hold each line */
int freq1[MAXC] = {0}, freq2[MAXC] = {0}; /* frequency arrays */
/* 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;
}
while (fgets (buf, MAXC, fp)) /* read each line */
if (sscanf (buf, " %c, %c", &c1, &c2) == 2) { /* parse values */
if (c1 > 0 || c2 > 0) /* validate ASCII values */
/* increment each element */
freq1[(unsigned char)c1]++, freq2[(unsigned char)c2]++;
}
else
fputs ("error: in line format.\n", stderr);
if (fp != stdin) fclose (fp); /* close file if not stdin */
puts ("lowercase occurrence:\n");
for (int i = 'a'; i <= 'z'; i++) /* loop over 'a' to 'z' */
printf (" %c: %d, %d\n", i, freq1[i], freq2[i]);
return 0;
}
И если вы хотитечем более подробный вывод, тем я оставляю вам возможность включить его в приведенный выше код.
Просмотрите все и дайте мне знать, если у вас есть дополнительные вопросы.