繁体   English   中英

不会从文件读取到结构

[英]Won't read from file to struct

我一直坐在这个问题上2天了,我无法弄清楚我做错了什么。 我已经尝试过调试(有点类似吗?还是有点新的),请点击此链接: https//ericlippert.com/2014/03/05/how-to-debug-small-programs/我试过Google和各种事情。 基本上我正在阅读这种格式的文件:

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;
 }

如果文件中的每一行都是单独的记录,则应将每行读作字符串,然后尝试解析每个字符串。

(请注意,这也具有推测性解析的附加功能:您可以尝试以几种不同的格式解析该行,并接受正确解析的行。我喜欢在接受例如矢量输入时使用它,以便用户可以使用xyzx, y, zx/y/z(x,y,z)[x,y,z]<xy z><x,y,z> ,依此类推他们喜欢。毕竟,每种格式只有一个额外的scanf。)

要读取行,可以将fgets()用于本地缓冲区。 本地缓冲区必须足够长。 如果程序仅在POSIX.1机器上运行(即不在Windows上运行),那么您可以使用getline()代替,它可以根据需要动态地重新分配给定的缓冲区,因此您不限于任何特定的行长度。

要解析字符串,请使用sscanf()

请注意,所有scanf系列函数中模式中的所有制表符,空格和换行符都完全相同:它们表示任意类型的任何空格。 换句话说, \\n并不意味着“然后换行” ; 它意味着与空间相同,即“这里可能还有一些空白” 但是,除%c%[之外的所有转换%[自动跳过任何前导空格; 因此,除了这两个中的一个之前的空间之外,模式中的空间仅对我们人类有意义,它们在扫描中没有任何功能效果。

所有scanf函数系列都返回成功转换的次数。 (唯一的例外是“转换” %n ,它产生消耗的字符数;某些实现包括转换计数,而其他一些则不包括。)如果输入结束发生在第一次转换或读取之前发生错误,或输入与模式的固定部分不匹配时,函数将返回EOF

即使您禁止保存转换结果 - 例如,如果您在输入中有一个您不需要的单词,您可以转换但丢弃它与%*s - ,它会被计算。 因此,例如sscanf(line, " %*d %*s %*d")如果行以整数开头,则返回3,后跟一个单词(任何不是换行符也不包含空格的单词),后跟一个整数。

而不是让函数返回解析的结构,传递指向结构的指针(和要读取的文件句柄),并返回状态代码。 我更喜欢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! */

}

上面的两个if子句是检查是否存在真正的读取错误(如,硬件或类似的问题),以及是否有未读/未解析的数据(不在文件末尾)。

与OP相比,扫描模式有两个不同之处:首先,所有解析的字符串都限制为3个字符,因为该结构每个只有3 + 1的空间。 字符串'\\0'的末尾保留一个字符,不计入%s的最大长度。 其次,我直接解析观众数,如果成功,只需将该字段乘以1000即可。

还要注意我是如何使用one->weekdayone->home_teamone->away_team来引用字符数组的。 这是有效的,因为可以使用数组变量,就好像它是指向该数组中第一个元素的指针一样。 (给定char a[5]; a&a&(a[0])都可以用于指代与第一元件的阵列中的a )。 我喜欢在扫描时使用这种“原始形式”,因为它可以更容易地将它们与%s转换匹配,并确保模式与参数匹配。

问题出在您的文件中。 它以空格开头,而不是像控制字符串中所述的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);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM