我已经和这个问题坐了2天,但我无法弄清楚我做错了什么。我已经尝试过调试(有点类似吗?还有一些新内容),请点击此链接:https://ericlippert.com/2014/03/05/how-to-debug-small-programs/我已经尝试了谷歌和各种各样的事情。基本上我是从这种格式的文件中读取的:
R1 Fre 17/07/2015 18.00 FCN - SDR 0 - 2 3.211
我必须让程序将其读入一个结构中,但是当我尝试打印信息时,它出错了。我的代码如下所示:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_INPUT 198
typedef struct game{
char weekday[4],
home_team[4],
away_team[4];
int round,
hour,
minute,
day,
month,
year,
home_goals,
away_goals,
spectators;}game;
game make_game(FILE *superliga);
int main(void){
int input_number,
number_of_games = 198,
i = 0;
game tied[MAX_INPUT];
FILE *superliga;
superliga = fopen("superliga-2015-2016.txt", "r");
for(i = 0; i < number_of_games; ++i){
tied[i] = make_game(superliga);
printf("R%d %s %d/%d/%d %d.%d %s - %s %d - %d %d\n",
tied[i].round, tied[i].weekday, tied[i].day, tied[i].month,
tied[i].year, tied[i].hour, tied[i].minute, tied[i].home_team,
tied[i].away_team, tied[i].home_goals, tied[i].away_goals,
tied[i].spectators);}
fclose(superliga);
return 0;
}
game make_game(FILE *superliga){
double spect;
struct game game_info;
fscanf(superliga, "R%d %s %d/%d/%d %d.%d %s - %s %d - %d %lf\n",
&game_info.round, game_info.weekday, &game_info.day, &game_info.month,
&game_info.year, &game_info.hour, &game_info.minute, game_info.home_team,
game_info.away_team, &game_info.home_goals, &game_info.away_goals,
&spect);
game_info.spectators = spect * 1000;
return game_info;
}
答案 0 :(得分:0)
如果文件中的每一行都是一个单独的记录,你应该将每一行读作一个字符串,然后尝试解析每个字符串。
(请注意,这也具有推测性解析的附加功能:您可以尝试以几种不同的格式解析该行,并接受正确解析的行。我喜欢在接受时使用它例如矢量输入,以便用户可以使用x y z
,x, y, z
,x/y/z
,(x,y,z)
,[x,y,z]
,<x y z>
,{{1}等等,取决于他们喜欢什么。毕竟,每种格式只有一个额外的scanf。)
要读取行,可以将fgets()
用于本地缓冲区。本地缓冲区必须足够长。如果程序仅在POSIX.1机器上运行(即不在Windows上运行),那么您可以使用getline()
代替,它可以根据需要动态地重新分配给定的缓冲区,因此您不仅限于任何特定的线长。
要解析字符串,请使用sscanf()
。
请注意,所有scanf系列函数中模式中的所有制表符,空格和换行符都完全相同:它们表示任意数量的任何类型的空格。换句话说,<x,y,z>
并不意味着&#34;然后是换行符&#34; ;它意味着与空间相同,即&#34;并且可能在这里有一些空白&#34; 。但是,除\n
和%c
之外的所有转换都会自动跳过任何前导空格;所以,除了这两个中的一个之前的空间外,模式中的空格只对我们人类有意义,它们在扫描中没有任何功能效果。
所有scanf系列函数都会返回成功转换的次数。 (唯一的例外是&#34;转换&#34; %[
,它产生了消耗的字符数;一些实现包括转换计数,而另一些则不包括。)如果输入结束发生在先前对于第一次转换,或发生读取错误,或者输入与模式的固定部分不匹配,函数将返回%n
。
即使您取消保存转换结果 - 例如,如果输入中有一个您不需要的单词,您可以转换但使用EOF
丢弃它 - ,它被计算在内。因此,例如%*s
如果行以整数开头,则返回3,后跟一个单词(任何不是换行符也不包含空格的单词),后跟一个整数。
不是让函数返回已解析的结构,而是将指针传递给结构(以及要读取的文件句柄),并返回状态代码。我更喜欢sscanf(line, " %*d %*s %*d")
取得成功,而非零失败,但我可以随意改变它。
换句话说,我建议你将阅读功能改为
0
要在循环中使用上述功能,请从标准输入(#ifndef GAME_LINE_MAX
#define GAME_LINE_MAX 1022
#endif
int read_game(game *one, FILE *in)
{
char buffer[GAME_LINE_MAX + 2]; /* + '\n' + '\0' */
char *line;
/* Sanity check: no NULL pointers accepted! */
if (!one || !in)
return -1;
/* Paranoid check: Fail if read error has already occurred. */
if (ferror(in))
return -1;
/* Read the line */
line = fgets(buffer, sizeof buffer, in);
if (!line)
return -1;
/* Parse the game; pattern from OP's example: */
if (sscanf(line, "R%d %3s %d/%d/%d %d.%d %3s - %3s %d - %d %d\n",
&(one->round), one->weekday,
&(one->day), &(one->month), &(one->year),
&(one->hour), &(one->minute)
one->home_team,
one->away_team,
&(one->home_goals),
&(one->away_goals),
&(one->spectators)) < 12)
return -1; /* Line not formatted like above */
/* Spectators in the file are in units of 1000; convert: */
one->spectators *= 1000;
/* Success. */
return 0;
}
)一个接一个地阅读游戏:
stdin
上面的两个game g;
while (!read_game(&g, stdin)) {
/* Do something with current game stats, g */
}
if (ferror(stdin)) {
/* Read error occurred! */
} else
if (!feof(stdin)) {
/* Not all data was read/parsed! */
}
条款用于检查是否存在真正的读取错误(例如,硬件问题或类似问题),以及是否存在未读/未解析的数据(不是在结尾处)文件),分别。
与OP相比,扫描模式有两个不同之处:首先,所有解析的字符串都限制为3个字符,因为该结构只有3 + 1的空间。一个字符保留用于字符串if
的末尾,不计入'\0'
的最大长度。其次,我直接解析观众数,如果成功,只需将该字段乘以1000。
另请注意我如何使用%s
,one->weekday
和one->home_team
来引用字符数组。这是有效的,因为可以使用数组变量,就好像它是指向该数组中第一个元素的指针一样。 (给定one->away_team
,char a[5];
和a
以及&a
都可用于引用数组&(a[0])
中的第一个元素。我喜欢使用这种&#34;原始形式&#34;扫描时,因为它可以更轻松地将它们与a
次转化匹配,并确保模式与参数匹配。
答案 1 :(得分:0)
问题出在您的档案中。它以空格开头,而不是如控制字符串中所述的R
。
检查fscanf()
的返回值,每次都会看到它为零。
如果你在fscanf()调用中添加一个前导空格,你的问题就会解决,如:
fscanf(superliga, " R%d %s %d/%d/%d %d.%d %s - %s %d - %d %lf\n",
&game_info.round, game_info.weekday, &game_info.day, &game_info.month,
&game_info.year, &game_info.hour, &game_info.minute, game_info.home_team,
game_info.away_team, &game_info.home_goals, &game_info.away_goals,
&spect);