试图协调多个数组之间的不同数据类型,通过数组索引将名称与分数同步,你会一直折磨自己。虽然这对于 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
这看起来就像您为玩家设计的程序一样。最好从可靠的数据处理开始,然后使用数组进行充电,结果发现它变得太笨拙而无法在以后进行大规模重写。如果您还有其他问题,请仔细查看并告诉我。