场景是这样的。
一个医疗中心需要将预约的详细信息存储在一个叫做 任命.数据。
它包括 患者姓名 和 任用类型。 任用类型可以是 "咨询, "扫描 或 '测试'. 文件中只存储第一个字母。
要求是:
appointment.dat
档案appointment.dat
在给定的示例格式下的文件。 Dave C
Ryan T
Mary S
George C
Julian S
appointment.dat
文件,并计算和显示给定格式下的病人数量。 Appointment Type Number of patients
Consulting 2
Scanning 2
Testing 1
下面是我尝试的代码。
#include <stdio.h>
int main()
{
int cCount, sCount, tCount;
char name, chan, C, S, T, c, s, t;
chan = " ";
cCount = sCount = tCount = 0;
FILE *cPtr;
cPtr = fopen ("appointment.dat", "r");
while (!feof(cPtr))
{
fscan (cPtr, "%s", &chan);
if (chan == C)
cCount++;
else if (chan == S)
sCount++;
else if (chan == T)
tCount++;
}
printf ("Appointment Type Number of patients\n");
printf ("Consulting %d \n");
printf ("Scanning %d \n");
printf ("Testing %d \n");
return 0;
}
我在显示部分有问题。病人数量显示一些地址,而不是病人数量。
我如何修改代码以使病人数量正确?
你已经掉进了大多数新C程序员最先掉进的陷阱之一。为什么while ( !feof (file) ) 总是错误的? 在你调用 fscan (cPtr, "%s", &chan);
(应该是 fscanf
),对于最后一行输入,读取成功,并且 EOF
没有设置。您测试 while (!feof(cPtr))
-- 而它不是。你再次循环,并达到 fscan (cPtr, "%s", &chan);
现在失败的原因是 输入失败 和 EOF
但你却盲目地继续检查 if (chan)
这时可能会失败,也可能看起来是正确的(在变量中增加一个错误的计数,对应于无论最后的 chan
是)。) 1
你进一步援引 未定义的行为 在你使用 printf
没有提供任何论据来证明这一点。"%d"
转换说明者 所载 格式化字符串例如
printf ("Consulting %d \n");
C11标准--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
.
现在你可以检查 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);
}
(注意: 你只需要一个 printf
语句,为每个连续的输出。C编译器将把所有相邻的以空格分隔的字符串串联起来,例如 "..."
"..."
变成一个单一的字符串)
例子 UseOutput
仅仅是为程序提供您的意见 stdin
使用bash 遗传性,你会得到。
$ 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-Standard没有定义什么是 chan
将在一个 输入失败 发生。C11标准--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++中的不确定行为?它与未定义行为有什么不同?