我是新来的所以在我的大学里,我只是学习c语言中的文件。我有一个任务。如果我将一个空文件放在我的项目目录中,并读取它。输出是符号(我不知道它是什么符号)。所以这是代码,请帮助
player dota[100];
FILE *fp;
fp = fopen("soal09.txt", "r");
if(fp == NULL)
{
printf("Error Opening The File!!\n");
return 0;
}
else
{
while(!feof(fp))
{
fscanf(fp, "%[^ ] %d %d\n", &dota[idx].name, &dota[idx].score, &dota[idx].num);
idx++;
}
}
fclose(fp);
do
{
enter();
menu();
printf("Input your choice [1..5]: ");
scanf("%d", &choose); fflush(stdin);
if(choose == 1)
{
system("cls");
enter();
printf("%-20s %-15s %s\n", "Player Name", ": Average Score", ": Number of Playing");
printf("====================================================================\n");
for(int i = 0; i < idx; i++)
{
printf("%-20s %-15d %d\n", dota[i].name, dota[i].score, dota[i].num);
}
printf("\nPress Enter to Continue...");
getchar();
}
getchar();
return 0;
}
输出是╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠-858993460
谢谢^^
最佳答案
仅在先前的文件I / O操作失败后才设置由feof()
检查的文件结束指示符。您必须尝试进行I / O操作,以查明是否已到达文件末尾。因此,对于一个空文件,您的代码尝试读取该文件,设置了文件结束指示符,第一个struct
中没有读取任何值,但是idx
递增了,所以看起来您已经添加了玩家的数据。但是第一个struct
的字段尚未初始化,因此您会看到垃圾。还要注意,dota[idx].name
大概是char
的数组,因此当传递给char
时,它会衰减为指向fscanf()
的指针。尽管似乎可以使用&dota[idx].name
,但是使用gcc -Wall -Wextra -pedantic
是错误的。确实会导致编译器发出警告,因此您应该启用这些警告(我总是至少使用feof()
)。
You should not use fscanf()
to control file I/O loops。一种简单的解决方案是使用player
的返回值来控制循环:
while(fscanf(fp, "%[^ ] %d %d\n",
dota[idx].name, &dota[idx].score, &dota[idx].num) == 3) {
idx++;
}
如果通过调用
struct
进行了三项分配,则只会更新fscanf()
struct
。但是,这种简单解决方案的问题在于它不能很好地处理格式错误的输入。如果数据文件的一行缺少字段,则fgets()
将被错误地填充,即使文件中还有更多行需要读取,循环也会终止。另外,由于没有为字符串转换指定字段宽度,因此长名称可能会导致程序崩溃。更好的解决方案是使用
sscanf()
将文件的行读取到缓冲区中,然后使用buffer
从缓冲区中提取信息:#include <string.h> // for strcpy()
...
char buffer[1000];
int line = 0;
char temp_name[100];
int temp_score, temp_num;
while(fgets(buffer, sizeof(buffer), fp)) {
++line;
if (sscanf(buffer, "%99[^ ] %d %d\n",
temp_name, &temp_score, &temp_num) == 3) {
strcpy(dota[idx].name, temp_name);
dota[idx].score = temp_score;
dota[idx].num = temp_num;
++idx;
} else if (buffer[0] != '\n') {
fprintf(stderr, "Bad input in line %d\n", line);
}
}
在这里,一个慷慨的
struct
被声明为接受文件中的一行文本,并且声明了临时变量以保存要存储在temp_name
字段中的值。我为struct
选择的大小为100,但这应与您实际的sscanf()
中字符串数组的大小匹配。请注意,%99s
中的字符串转换的字段宽度为99,因此最多匹配99个非空格(不是非空格,为什么不在这里仅使用'\0'
?)字符匹配,从而留出空格要添加的fgets()
。NULL
到达文件末尾时将返回line
,因此循环将继续直到发生这种情况。对于读取的每一行,sscanf()
计数器都会递增。然后,使用sscanf()
将数据读入临时变量。检查从struct
返回的值以确保已进行3个分配,如果是,则将数据复制到idx
,并递增strcpy()
。请注意,需要使用temp_name
将字符串从dota[idx].name
复制到sscanf()
。如果从
buffer
返回的值表示进行了3项分配以外的操作,则检查stderr
是否保留空行。如果不是,则会在do
上显示一条错误消息,提供文件中错误输入的行号。还有更多意见。您的
while()
循环似乎缺少关联的fflush(stdin)
。然后在scanf()
循环内的do
之后使用fflush()
。 fflush()
用于刷新输出流。在C标准(ISO / IEC 9899:2011 7.21.5.2/2)中明确未定义fflush(stdin)
在输入流上的行为,尽管我认为Microsoft在这里与标准有所不同。但是,您不应在可移植的C代码中使用'\n'
。相反,请使用以下内容:int c;
...
scanf("%d", &choose);
while ((c = getchar()) != '\n' && c != EOF)
continue; // discard extra characters
此代码从输入流中读取字符,直到到达
EOF
或scanf()
,从输入流中清除上一次对关于c - 读取空文件,输出为符号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41205858/