用空行读取文本文件

时间:2019-05-16 02:23:02

标签: c file scanf

该文本文件可写,其结构后有1个空白行。
类似于:

1  111 1 Peter

22 22  2 John Lays

3  3   3 Anne Belgs

结构为:

struct estruturaCarro {
    int id, potencia, avariado;
    char name[11]; 
} carro;

我已将数据写入文本文件:

fprintf(fp, "%-2d %-3d %-1d %-10s \n\n", carro.id, carro.potencia, carro.avariado, carro.name);

我以这种方式读取文件数据,但是我确定这不是最好的方式:

while(true){
    int result = fscanf(fp, "%d %d %d %10[^\n]", &carro.id, &carro.potencia, &carro.avariado, &carro.name);
    if(result==4){    
            printf("%-2d %-3d %-1d % -s\n", carro.id, carro.potencia, carro.avariado, carro.name);
    }else break;
}

如何在不读取空白行的情况下读取文本文件并保存数据结构?

如果我想验证值(ID = xx,potencia = xxx,avariado = x,name = 10个字符),在填充struct数组之前还是之后更好? br />文件的每一行的格式为:
xx xxx x aaaaaaaaaa(x =数字,a =字符)
例如,如果其中一行是
9 123 4 Error 1st value
停止读取文件(通知用户),因为该行应为:
9 123 4 Error 1st value(9后缺少一个空格)-或由于NAME的字符超过10个。

2 个答案:

答案 0 :(得分:1)

您没有显示如何在true中设置while(true),但是它必须基于您接近fscanf的返回值。您必须测试三种情况:

  1. fscanf返回为EOF,已读完成,true应该设置为FALSE才能中断循环;
  2. fscanf返回少于4 matching input 失败,不能保证由于空行而发生;和
  3. fscanf的回报等于4,很好的输入。

除非您同时检查所有三个情况,否则无法涵盖fscanf的所有情况,此外,如果输入文件中有任何其他字符或多余字符,则格式化的读取将失败。

这就是为什么更好的选择是使用fgets将每一行读入缓冲区,然后使用sscanf解析缓冲区本身所需的信息。这提供了允许单独验证(1)读取的好处; (2)信息解析。此外,由于您一次消耗一行,因此对输入流中剩余的内容没有任何不确定性,并且任何一行的解析失败都不会阻止成功读取其余部分。

一个简短的实现,其中fgets()sscanf()将每个结构读入结构数组,您可以执行以下操作:

#include <stdio.h>

#define MAXN   11   /* if you need a constant, #define one (or more) */
#define MAXC 1024   /*         (don't skimp on buffer size)          */

typedef struct {    /* use a simple typedef */
    int id, potencia, avariado;
    char name[MAXN]; 
} carro_t;

int main (int argc, char **argv) {

    char buf[MAXC];                         /* buffer to read line */
    carro_t carro[MAXN] = {{ .id = 0 }};    /* array of struct  */
    size_t ndx = 0;                         /* array index      */
    /* 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 (ndx < MAXN && fgets (buf, MAXC, fp)) {   /* read each line */
        carro_t tmp = { .id = 0 };                  /* temp struct */
        if (*buf == '\n')           /* if line empty */
            continue;               /* get next line */
        if (sscanf (buf, "%d %d %d %10[^\n]",   /* separate/validate */
                    &tmp.id, &tmp.potencia, &tmp.avariado, tmp.name) == 4)
            carro[ndx++] = tmp;     /* add to array of struct */
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    for (size_t i = 0; i < ndx; i++)
        printf ("%3d  %3d  %3d  %s\n",
            carro[i].id, carro[i].potencia, carro[i].avariado, carro[i].name);

    return 0;
}

注意:文件名作为程序的第一个参数提供,或者,如果未提供文件名,则默认从stdin读取)

尽管使用特定的数据文件,但没有理由不能使用fscanf,它很脆弱,并且一个字符太多(例如"Anne Belgss")会导致它损坏。一个fscanf实现删除了true并按照您的需要简单地循环:

    for (;;) {
        carro_t tmp = { .id = 0 };
        if (fscanf (fp, "%d %d %d %10[^\n]",    /* separate/validate */
                    &tmp.id, &tmp.potencia, &tmp.avariado, tmp.name) == 4)
            carro[ndx++] = tmp;     /* add to array of struct */
        else
            break;
    }

无论哪种方式,您都将使用“ this”输入文件产生相同的输出,例如

使用/输出示例

$ ./bin/readwblanks ~/tmpd/file
  1  111    1  Peter
 22   22    2  John Lays
  3    3    3  Anne Belgs

答案 1 :(得分:0)

只用简单的阅读方式就可以了,

    while(fscanf(fp,"%d %d %d %s",&carro.id,&carro.potencia,&carro.avariado,carro.name)!=EOF)
    printf("%d %d %d %s\n",carro.id,carro.potencia,carro.avariado,carro.name);

结果是这样的 enter image description here