【问题标题】:Syntax when using fscanf to fill a 2D array使用 fscanf 填充二维数组时的语法
【发布时间】:2020-08-10 10:43:25
【问题描述】:

我目前正在尝试用 C 语言设计一个程序,该程序可以从文本文件中获取玩家的姓名和他们的分数。 name 数组和 score 数组都是 2D 的,有助于在以后更轻松地显示高分。 现在我的 fscanf 设置如下:

while(feof(file_p) == 0){

fscanf(file_p,"%s %d\n", name[r][c],score[r][c]);
r++;
}

如果我没记错的话,%s 通常意味着类似

  fscanf(file_p,"%s %d\n", name,score);

我只是不知道如何控制使用这种格式将每个名称放入哪一行。

输入

John 10
Bob 20
Sue 90

预期输出

name[0][] = John    score[0][0] = 10
name[1][] = Bob     score[1][0] = 20
name[2][] = Sue     score[2][0] = 30

这里还有一些代码。我使用临时 1D 数组和 strcpy 将其转换为我的 2D 数组,但我不熟悉字符串库,所以也没有解决。

   void displayHighScores(FILE*fp){
   char name[MAX][MAX]; 
   int i = 0,x = 0,score[MAX][MAX]; 
   char temp_name[MAX];
    int temp_score;
    // Gather scores from the Document
    fp = fopen(FILENAME, "r"); 
   while (feof(fp) == 0) {
    //gets info from Document and stores in temp variables
        fscanf(fp,"%s %d\n", temp_name,&temp_score); 
   i++; 
   }
   }

感谢您的帮助!

【问题讨论】:

  • name 数组是如何构造的?你得到什么输出?也许这不足以理解您的问题。
  • 每次我尝试解决这个问题时都会遇到编译器错误。我不确定 %s 在二维数组中的表现如何,尤其是在我使用 fscanf() 时。名称数组被定义为二维数组,因此 char name[MAX][MAX] MAX 是一个宏 = 100
  • 不要使用feof,使用来自fscanf 的返回值控制循环,您已忽略该值但对于正确运行至关重要。 while(fscanf(...) == 2) { }请看Why is while ( !feof (file) ) always wrong?
  • 看起来您正在向fscanf 提供数组定义。请发布Minimal Reproducible Example,这是显示问题的最短完整代码。我还建议您使用tour 并阅读How do I ask a good question?
  • 请通过编辑问题发布代码,而不是在 cmets 中。您还没有时间阅读tour并阅读How do I ask a good question?

标签: c multidimensional-array file-io c-strings


【解决方案1】:

试图协调多个数组之间的不同数据类型,通过数组索引将名称与分数同步,你会一直折磨自己。虽然这对于 2-values 是可行的,但它会疯狂地失控并变得无法管理。相反,每当您需要在 C 中将多个不同类型的值作为单个对象进行协调时,您应该考虑struct

在这里,如果您定义一个简单的结构来存储球员姓名、分数数组以及为该球员存储的分数数量的计数器,您可以简单地在程序中为球员声明一个结构数组,然后为您的玩家将数据读入每个结构。这样,每个玩家的名称和尽可能多的分数都会在单个struct 中进行协调。例如,您可以将结构声明为:

/* if you need a constant, #define them or use an enum */
enum { NSCORES = 10, MAXNAME = 64, NPLAYER = 128, MAXLINE = 256 };

typedef struct {                /* simply struct for a player */
    char name[MAXNAME];         /* using a typedef for convenience */
    int ns, scores[NSCORES];    /* ns tracks the number of scores */
} player_t;

注意:typedef 提供了一种简单的便利,不必在使用结构的任何地方的名称前输入struct

然后您可以使用 fgets() 或 POSIX getline() 将每一行读入缓冲区,然后从缓冲区解析每个玩家的名称和得分。您可以使用多种方法将缓冲区中的值拆分为名称和提供的尽可能多的核心。在这种情况下,对于简单的名字,使用sscanf() 并在缓冲区中保留您的位置的偏移量以读取所有剩余分数可能同样容易。

(将行拆分为值的另一种选择是使用一对指针将每个值括起来以进行提取,使用strtok() 函数(或strsep())对行进行标记,或者将指针与strchr()strspn()strcspn() -- 由你决定)

您可以使用fgets() 读取该行并使用sscanf() 分隔值,类似于:

int main (void) {

    char buf[MAXLINE];          /* buffer to read entire line of input */
    size_t n = 0;               /* counter for number of players read */
    player_t player[NPLAYER] = {{ .name = "" }};    /* array of players */

    /* while array not full, read each line of input into buf */
    while (n < NPLAYER && fgets (buf, MAXLINE, stdin)) {
        int offset;     /* to keep offset from beginning of buf */
        /* read name from buf, save no. of chars read in offset */
        if (sscanf (buf, "%s%n", player[n].name, &offset) == 1) {
            int chars;  /* to track characters consumed for each score */
            /* read score, save no. of chars read */
            while (player[n].ns < NSCORES && sscanf (buf + offset, "%d%n",
                    &player[n].scores[player[n].ns], &chars) == 1) {
                offset += chars;    /* update offset with chars read */
                player[n].ns++;     /* update no. of scores for player */
            }
        }
        if (player[n].ns)       /* if scores stored for player */
            n++;                /* increment player index to next player */
    }

(注意:读取name所消耗的字符直接存储在offset中(因为此时没有偏移量),但是读取转换每个数字的字符数是存储在chars,因此可以将其添加到当前的offset以供下次读取)

总而言之,输出每个玩家的姓名和得分的基本示例可能是:

#include <stdio.h>

/* if you need a constant, #define them or use an enum */
enum { NSCORES = 10, MAXNAME = 64, NPLAYER = 128, MAXLINE = 256 };

typedef struct {                /* simply struct for a player */
    char name[MAXNAME];         /* using a typedef for convenience */
    int ns, scores[NSCORES];    /* ns tracks the number of scores */
} player_t;

int main (void) {

    char buf[MAXLINE];          /* buffer to read entire line of input */
    size_t n = 0;               /* counter for number of players read */
    player_t player[NPLAYER] = {{ .name = "" }};    /* array of players */

    /* while array not full, read each line of input into buf */
    while (n < NPLAYER && fgets (buf, MAXLINE, stdin)) {
        int offset;     /* to keep offset from beginning of buf */
        /* read name from buf, save no. of chars read in offset */
        if (sscanf (buf, "%s%n", player[n].name, &offset) == 1) {
            int chars;  /* to track characters consumed for each score */
            /* read score, save no. of chars read */
            while (player[n].ns < NSCORES && sscanf (buf + offset, "%d%n",
                    &player[n].scores[player[n].ns], &chars) == 1) {
                offset += chars;    /* update offset with chars read */
                player[n].ns++;     /* update no. of scores for player */
            }
        }
        if (player[n].ns)       /* if scores stored for player */
            n++;                /* increment player index to next player */
    }

    for (size_t i = 0; i < n; i++) {                /* loop over players */
        printf ("%-10s", player[i].name);           /* output name */
        for (int j = 0; j < player[i].ns; j++)      /* loop over scores */
            printf (j ? ", %d" : "%d", player[i].scores[j]);  /* output scores */
        putchar ('\n');         /* tidy up with newline */
    }

}

示例输入

为了展示如何为每个玩家捕获多个(和不同数量的)分数,输入为每个玩家提供不同数量的分数:

$ cat dat/players_scores.txt
John 10 11   12   17
Bob 20  30   40   50  60  70
Sue 90 91 92

注意:由于"%d"sscanf()转换说明符忽略了前导空格,所有空格都会被忽略)

使用/输出示例

文件只是在stdin 上重定向到程序(文件处理留给你,虽然它只需要获取要读取的文件名,调用fopen(),然后验证文件是否已打开以供读取和将fgets() 调用中的stdin 更改为您的FILE* 指针)

$ ./bin/players_scores < dat/players_scores.txt
John      10, 11, 12, 17
Bob       20, 30, 40, 50, 60, 70
Sue       90, 91, 92

这看起来就像您为玩家设计的程序一样。最好从可靠的数据处理开始,然后使用数组进行充电,结果发现它变得太笨拙而无法在以后进行大规模重写。如果您还有其他问题,请仔细查看并告诉我。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-27
    • 1970-01-01
    • 2014-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多