Вы попали в одну из первых ловушек, в которую попадают самые новые C программисты. Почему while (! Feof (file)) всегда неверно? При вызове fscan (cPtr, "%s", &chan);
(который должен быть fscanf
), для последней строки ввода, читаем успешно и EOF
не установлено. Вы тестируете while (!feof(cPtr))
- и это НЕ. Вы снова наберете oop и достигнете fscan (cPtr, "%s", &chan);
, который теперь не срабатывает из-за сбоя ввода и EOF
возвращается - но вы слепо переходите к проверке if (chan)
, которая может дать сбой в этот момент или может показаться, что работает правильно (добавление дополнительного ошибочного счетчика к переменной, соответствующей тому, что было последним значением chan
). 1
Вы также вызываете Неопределенное поведение при использовании printf
, не предоставив аргумента для "%d"
спецификатор преобразования , содержащийся в строке формата , например,
printf ("Consulting %d \n");
C11 Standard - 7.21.6.1 Функция fprintf (p2) (это объясняет вашу «Количество пациентов отображает некоторые адреса вместо количества пациентов.» )
Когда вы читаете строку ввода одновременно, используйте ориентированную на строку функция ввода, например fgets()
или POSIX getline()
, для чтения всей строки в массив достаточного размера, а затем использование sscanf()
для разделения массива на необходимые значения, например,
#define MAXC 128 /* if you need a constant, #define one (or more) */
...
char buf[MAXC], name[MAXC], type;
...
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
if (sscanf (buf, "%s %c", name, &type) == 2) { /* split buf into name, type */
Вы используете return самой функции чтения для управления продолжением чтения -l oop. Вы не можете правильно использовать любую функцию пользовательского ввода без Проверка return
.
Теперь вы можете проверить type
. Простой способ - использовать оператор switch()
- хотя в последовательности операторов if()
нет ничего плохого. Вы можете использовать switch()
, подобный следующему:
switch (type) { /* switch on type */
case 'C': C++; break;
case 'S': S++; break;
case 'T': T++; break;
default:
fprintf (stderr, "error: invalid type '%c'\n", type);
break;
}
Полностью подставив его и разрешив передать имя файла в качестве первого аргумента программе (или прочитать из stdin
по умолчанию, если имя файла отсутствует дано), вы можете сделать:
#include <stdio.h>
#define MAXC 128 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char buf[MAXC], name[MAXC], type;
size_t C = 0, S = 0, T = 0;
/* 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 into buf */
if (sscanf (buf, "%s %c", name, &type) == 2) { /* split buf into name, type */
switch (type) { /* switch on type */
case 'C': C++; break;
case 'S': S++; break;
case 'T': T++; break;
default:
fprintf (stderr, "error: invalid type '%c'\n", type);
break;
}
}
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
printf ("Appointment Type Number of patients\n" /* output results */
"Consulting %9zu\n"
"Scanning %9zu\n"
"Testing %9zu\n", C, S, T);
}
( note: вам нужен только один оператор printf
для каждого непрерывного вывода. Компилятор C объединит все соседние строки, разделенные пробелами Например, "..."
"..."
в одну строку)
Пример использования / Вывод
Просто введите ваш ввод для программы на stdin
с помощью bash heredo c, вы получите:
$ cat << eof | ./bin/appointments
> Dave C
> Ryan T
> Mary S
> George C
> Julian S
> eof
Appointment Type Number of patients
Consulting 2
Scanning 2
Testing 1
Чтение из файла
С вашими данными в файле dat/appointment_types.txt
Например,
$ cat dat/appointment_types.txt
Dave C
Ryan T
Mary S
George C
Julian S
Ваше использование и вывод будет следующим:
$ ./bin/appointments dat/appointment_types.txt
Appointment Type Number of patients
Consulting 2
Scanning 2
Testing 1
Просмотрите все и дайте мне знать, если у вас есть дополнительные вопросы.
Сноски:
1. Стандарт C не определяет, что chan
будет удерживать после сбоя ввода происходит. C11 Standard - 7.21.6.2 Функция fscanf (p9) Поведение просто не определено, поскольку chan
является неопределенным в этой точке и используется, пока оно имеет неопределенное значение. Стандарт C11 - J.2 Неопределенное поведение "The value of an object with automatic storage duration is used while it is indeterminate (6.2.4, 6.7.9, 6.8)."
и см. Обсуждение Неопределенное, неопределенное и определяемое реализацией поведение и Что такое неопределенное поведение в C ++? Чем он отличается от неопределенного поведения?