首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C-使用fget或sscanf读取多行文件?

C-使用fget或sscanf读取多行文件?
EN

Stack Overflow用户
提问于 2020-06-11 17:08:07
回答 2查看 827关注 0票数 1

比如说,我有一个文本文件

代码语言:javascript
运行
复制
person1
25
500
male
person2
..
..
..
person3
..

不管有多少人,我想把文件的4行读成一个结构,供文件中的每个人使用。

我该怎么做?我尝试过使用多个fget,但是我不知道如何循环到文件的末尾,同时每次读取四行。

谢谢

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-06-11 18:52:16

继续上面的注释,当您需要将数据文件中的固定重复行集读入struct时,这是唯一的例外,您应该考虑scanf()/fscanf()而不是建议的每一行的fgets()/sscanf()

为什么?

scanf()是一个格式化的输入函数(与面向行的输入函数fgets()相比)。如果您有跨多行的格式化输入,scanf()/fscanf()将忽略空格( '\n'字符为空白),并允许您将多行作为单个输入(使用精心编制的格式字符串)。

当使用scanf()/fscanf()将数据读入字符串(或数组)时,必须使用字段宽度修饰符来限制读取到数组中的值的数量,以避免在输入超过数组界限时调用未定义行为的数组末尾写入。每当您使用scanf()/fscanf()/sscanf() (整个家族)时,这都适用。使用没有字段宽度修饰符来读取数组数据并不比使用gets()更好。

那么如何制作您的格式字符串呢?让我们看一个包含4个成员的示例结构,类似于您在问题中所显示的内容。

代码语言:javascript
运行
复制
...
#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类型,则可以执行以下类似操作:

代码语言:javascript
运行
复制
    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 */

具体查看格式字符串时,您有:

代码语言:javascript
运行
复制
    " %127[^\n]%d%d %7[^\n]"

如果" %127[^\n]"凭借前导' '使用任何前导空格,然后最多读取127字符(不能使用变量或宏来指定字段宽度),则字符是行中任何不是'\n'字符的字符(允许将空白作为名称的一部分读取,例如"Mickey Mouse")。

注意,"%[...]是一个字符串转换,它将将字符列表中的任何字符[...]作为字符串读取。使用'^'作为列表的第一个字符将导致"%[^\n]"将不包括'\n'的所有字符读取到字符串中。

" %[^\n]"之前的空格是必需的,因为"%[...]" (如"%c" )是唯一不使用前导空格的转换说明符,因此您可以在格式字符串中的转换之前包含一个空格来提供这一点。另外两个用于int的转换说明符(例如,"%d" )将自己占用前导空格,从而导致总转换:

代码语言:javascript
运行
复制
    " %127[^\n]%d%d %7[^\n]"

总之,这将:

%127[^\n];

  • readi中包含第一个整数值的一行由weight左转到iq >d55(这消耗了whitespace);

  • read将包含第二个整数值的行引导到weight%d (ditto);

  • ' '使用weight左的'\n';最后,

  • %7[^\n] (根据需要调整以保持最长的性别字符串)

将最多7个字符的一行读入gender成员中。

使用这种方法,您可以使用数组中每个结构的4行输入,只需调用一次fscanf()。您应该检查rtn on循环出口,以确保在读取文件中的所有值后,循环在EOF上退出。一个简单的检查将涵盖所需的最小验证。

代码语言:javascript
运行
复制
    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,以查看循环退出的原因是否是由于单独的数组已满)。

总之,你可以这样做:

代码语言:javascript
运行
复制
#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来定义常量)

示例输入文件

代码语言:javascript
运行
复制
$ cat dat/ppl.txt
person1
25
500
male
person2
128
128
female
Mickey Mouse
56
2
male
Minnie Mouse
96
1
female

示例使用/输出

代码语言:javascript
运行
复制
$ ./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()的单独调用--再加上处理线路计数器或多缓冲区逻辑的附加逻辑,将开始增加。

仔细考虑一下,如果你还有其他问题,请告诉我。

票数 1
EN

Stack Overflow用户

发布于 2020-06-11 17:32:49

一些例子行。剩下的部分我就交给你了。

代码语言:javascript
运行
复制
#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 */
    }

    ....
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62329772

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档