比如说,我有一个文本文件
person1
25
500
male
person2
..
..
..
person3
..不管有多少人,我想把文件的4行读成一个结构,供文件中的每个人使用。
我该怎么做?我尝试过使用多个fget,但是我不知道如何循环到文件的末尾,同时每次读取四行。
谢谢
发布于 2020-06-11 18:52:16
继续上面的注释,当您需要将数据文件中的固定重复行集读入struct时,这是唯一的例外,您应该考虑scanf()/fscanf()而不是建议的每一行的fgets()/sscanf()。
为什么?
scanf()是一个格式化的输入函数(与面向行的输入函数fgets()相比)。如果您有跨多行的格式化输入,scanf()/fscanf()将忽略空格( '\n'字符为空白),并允许您将多行作为单个输入(使用精心编制的格式字符串)。
当使用scanf()/fscanf()将数据读入字符串(或数组)时,必须使用字段宽度修饰符来限制读取到数组中的值的数量,以避免在输入超过数组界限时调用未定义行为的数组末尾写入。每当您使用scanf()/fscanf()/sscanf() (整个家族)时,这都适用。使用没有字段宽度修饰符来读取数组数据并不比使用gets()更好。
那么如何制作您的格式字符串呢?让我们看一个包含4个成员的示例结构,类似于您在问题中所显示的内容。
...
#define MAXG 8 /* if you need a constant, #define one (or more) */
#define MAXP 32
#define MAXN 128
typedef struct { /* struct with typedef */
char name[MAXN], gender[MAXG];
int iq, weight;
} person;
...如果您的数据如图所示,name声明为128字符,gender声明为8字符,其余两个成员为int类型,则可以执行以下类似操作:
int rtn; /* fscanf return */
size_t n = 0; /* number of struct filled */
person ppl[MAXP] = {{ .name = "" }}; /* array of person */
...
while (n < MAXP && /* protect struct array bound, and each array bound below */
(rtn = fscanf (fp, " %127[^\n]%d%d %7[^\n]", /* validate each read */
ppl[n].name, &ppl[n].iq, &ppl[n].weight, ppl[n].gender)) == 4)
n++; /* increment array index */具体查看格式字符串时,您有:
" %127[^\n]%d%d %7[^\n]"如果" %127[^\n]"凭借前导' '使用任何前导空格,然后最多读取127字符(不能使用变量或宏来指定字段宽度),则字符是行中任何不是'\n'字符的字符(允许将空白作为名称的一部分读取,例如"Mickey Mouse")。
注意,"%[...]是一个字符串转换,它将将字符列表中的任何字符[...]作为字符串读取。使用'^'作为列表的第一个字符将导致"%[^\n]"将不包括'\n'的所有字符读取到字符串中。
" %[^\n]"之前的空格是必需的,因为"%[...]" (如"%c" )是唯一不使用前导空格的转换说明符,因此您可以在格式字符串中的转换之前包含一个空格来提供这一点。另外两个用于int的转换说明符(例如,"%d" )将自己占用前导空格,从而导致总转换:
" %127[^\n]%d%d %7[^\n]"总之,这将:
%127[^\n];
weight左转到iq >d55(这消耗了whitespace);
weight和%d (ditto);
' '使用weight左的'\n';最后,
%7[^\n] (根据需要调整以保持最长的性别字符串)
将最多7个字符的一行读入gender成员中。
使用这种方法,您可以使用数组中每个结构的4行输入,只需调用一次fscanf()。您应该检查rtn on循环出口,以确保在读取文件中的所有值后,循环在EOF上退出。一个简单的检查将涵盖所需的最小验证。
if (rtn != EOF) /* if loop exited on other than EOF, issue warning */
fputs ("warning: error in file format or array full.\n", stderr);(注意:,您也可以检查n == MAXP,以查看循环退出的原因是否是由于单独的数组已满)。
总之,你可以这样做:
#include <stdio.h>
#define MAXG 8 /* if you need a constant, #define one (or more) */
#define MAXP 32
#define MAXN 128
typedef struct { /* struct with typedef */
char name[MAXN], gender[MAXG];
int iq, weight;
} person;
int main (int argc, char **argv) {
int rtn; /* fscanf return */
size_t n = 0; /* number of struct filled */
person ppl[MAXP] = {{ .name = "" }}; /* array of person */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (n < MAXP && /* protect struct array bound, and each array bound below */
(rtn = fscanf (fp, " %127[^\n]%d%d %7[^\n]", /* validate each read */
ppl[n].name, &ppl[n].iq, &ppl[n].weight, ppl[n].gender)) == 4)
n++; /* increment array index */
if (rtn != EOF) /* if loop exited on other than EOF, issue warning */
fputs ("warning: error in file format or array full.\n", stderr);
for (size_t i = 0; i < n; i++) /* output results */
printf ("\nname : %s\niq : %d\nweight : %d\ngender : %s\n",
ppl[i].name, ppl[i].iq, ppl[i].weight, ppl[i].gender);
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}(注意:您也可以使用全局enum来定义常量)
示例输入文件
$ cat dat/ppl.txt
person1
25
500
male
person2
128
128
female
Mickey Mouse
56
2
male
Minnie Mouse
96
1
female示例使用/输出
$ ./bin/readppl dat/ppl.txt
name : person1
iq : 25
weight : 500
gender : male
name : person2
iq : 128
weight : 128
gender : female
name : Mickey Mouse
iq : 56
weight : 2
gender : male
name : Minnie Mouse
iq : 96
weight : 1
gender : female您也可以使用行计数器或多行读取方法使用fgets()读取每一行,但这更多地是关于选择合适的工作工具。使用fgets(),然后对整数值多次调用sscanf(),或者对strtol()进行两个调用以进行转换没有什么问题,但是对于大输入文件,对fscanf()的1-函数调用与对fgets() +2的4次单独调用相比--对sscanf()或strtol()的单独调用--再加上处理线路计数器或多缓冲区逻辑的附加逻辑,将开始增加。
仔细考虑一下,如果你还有其他问题,请告诉我。
发布于 2020-06-11 17:32:49
一些例子行。剩下的部分我就交给你了。
#define MAX 1000
...
FILE *f;
char line1[MAX], line2[MAX], line3[MAX], line4[MAX];
...
while(fgets(line1, MAX, f) != NULL)
{
if (fgets(line2, MAX, f) == NULL ||
fgets(line3, MAX, f) == NULL ||
fgets(line4, MAX, f) == NULL)
{
/* insert code here to handle end of file in unexpected place */
break;
}
/* insert code here to do your sscanf and anything else you want */
}
....https://stackoverflow.com/questions/62329772
复制相似问题