例如,我有一个文本文件
person1
25
500
male
person2
..
..
..
person3
..
不管有多少人,我想把文件的4行读入文件中每个人的结构中。
我该怎么做?我试过只使用多个fget,但我不知道如何在每次读取四行的同时循环到文件末尾
感谢
继续上面的注释,当数据文件中有一组固定的重复行需要读取到struct
中时,这是唯一一个例外,您应该将scanf()/fscanf()
考虑到每行的推荐fgets()/sscanf()
之上。
为什么
scanf()
是一个格式的输入函数(与fgets()
相比,CCD_5是一个面向行的输入函数)。如果您格式化了跨多行的输入,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]"
总之,这将:
- 使用任何前导空格(上一次读取后留在
stdin
中的'n'
或数组中上一个结构的gender
) - 用CCD_ 35向CCD_ 34成员读取最多127个字符的行
- 将包含第一整数值的行读取到具有
%d
的iq
中(其消耗前导空白) - 用
%d
将包含第二整数值的行读取到weight
中(同上) - CCD_ 40消耗CCD_;最后
- 用
%7[^n]
在gender
成员中读取最多7个字符的行(根据需要进行调整以保持最长的性别字符串)
使用这种方法,只需对fscanf()
进行一次调用,就可以在数组中的每个结构中使用4行输入。您应该在循环退出时检查rtn
,以确保从文件中读取所有值后,循环在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 : %sniq : %dnweight : %dngender : %sn",
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()
的4个单独调用相比)加上对sscanf()
或strtol()
的2个单独调用(加上用于处理行计数器或多缓冲区逻辑的附加逻辑)将开始相加。
仔细看看,如果你还有问题,请告诉我。
一些示例行。剩下的节目我交给你。
#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 */
}
....