c - 读取带有空行的文本文件

标签 c file scanf

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

1  111 1 Peter

22 22  2 John Lays

3  3   3 Anne Belgs

The struct is:

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个字符),在填充结构体数组之前还是之后更好?< br/>文件每一行的格式为:
xx xxx x aaaaaaaaaa(x=数字,a=字符)
例如,如果其中一行是
9 123 4 第一个值错误
停止读取文件(通知用户),因为该行应该是:
9 123 4 第一个值错误(9 后缺少一个空格) - 或因为 NAME 超过 10 个字符。

最佳答案

你没有展示如何true设置于while(true) ,但必须基于 fscanf 的返回以你接近它的方式。您必须测试三种情况:

  1. fscanf返回是EOF ,读完,true应设置为FALSE打破循环;
  2. fscanf返回小于4匹配输入失败,不保证这种情况是由于空行而发生的;和
  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;
    }

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

示例使用/输出

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

关于c - 读取带有空行的文本文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56159936/

相关文章:

c - 使用 sscanf() 拆分 AT 命令字符串

c - fscanf() 读取带格式行空格的字符串

c - 从链表中删除节点问题C

c++ - 在两个任务之间使用互斥信号量

file - 导出行记录长度大于32767个字符的txt文件?

php - 如何使用 ob_start append 到文件

php - 在 PHP 中编辑 .doc

c - 第一种情况和第二种情况有什么区别?

c - 文件读取返回重复两次

c - scanf checkin while 循环以限制整数输入