0

几天前我开始了一个小项目,我应该做两件事:

  1. 创建一个函数来读取文件信息并将其显示在屏幕上。
  2. 创建一个函数以将新记录写入文件。(添加新记录)

我的结构

struct cofeeShop
{
char cofeeName [20]
char cofeeColor [20]
chat cofeeWorld [20]
int CofeePockets
int onePocketSize 
}

包含以下信息的 txt 文件“records.txt”:

Lavazza Gray Europe 433 10 
Machito Black Europe 433 10 
Machito White Asia 24 18 
Chiley Black Asia 198 17 
Hucki White America 11 11

我是 C 编程新手,因此我需要一些帮助。

我知道 C 语言有 4 种不同的扫描方法(fscanf、fgets、fgetc、freada),我不知道在我的情况下哪种方法最适合结构扫描。主要区别是什么?

我需要一个数组来在屏幕上显示文件中的所有数据吗?如何将文件txt中的所有数据显示到屏幕上?

有什么建议吗?

谢谢你的时间!

4

1 回答 1

0

如果您想要一个简短的示例来帮助您入门,那么在将字符串读入字符数组(缓冲区)时要记住的第一件事是不要吝啬缓冲区大小!. 使用10and20可能会为您显示的一小部分数据削减它,但是当您尝试阅读时会发生什么Mandheling Black?使用您最长的预期字符串,并且在使用固定数组时至少将字符数加倍(如果它是一个要重复使用的缓冲区,例如读取每一行,1K 缓冲区大小就可以了,2K 也可以)

尽量避免使用像and之类的MagicNumbers,而是:1020

#include <stdio.h>

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

typedef struct cofeeshop {      /* typedef avoid writing 'struct name' in code */
    char cofeeName [MAXNM],
        cofeeColor [MAXNM],
        cofeeWorld [MAXNM];
    int CofeePockets,
        onePocketSize; 
} cofeeshop;
...

如果您在上面注意到, a typedefofstruct cofeeshop是在 name 中创建的cofeeshop。通过创建typedef,您可以简单地将cofeeshop其用作代码中任何需要的类型,而不必struct cofeeshop每次都编写。

您需要从文件中实际读取的是一个足够大以容纳每一行的缓冲区(字符数组),一个用于跟踪您已读取多少咖啡类型的计数器,然后是一个cofeeshop用于保存每个包含的值的数组线。您可以这样做,同时将要读取的文件名作为程序的第一个参数(或者stdin如果没有给出参数,则从读取),如下所示:

int main (int argc, char **argv) {
    
    char line[MAXC];                                        /* buffer to hold each line */
    cofeeshop cofee[MAXCT] = {{.cofeeName = ""}};           /* array of chofeeshop */
    size_t n = 0;                                           /* cofeetype counter */
    /* 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;
    }
    ...
    

请注意,在您尝试从文件中读取之前,始终验证使用的任何输入/输出函数的返回,包括fopen()成功或失败。

然后,您可以将文件中的每一行读取fgets()到您的缓冲区中line,然后传递line给以sscanf()将部分分隔line到结构数组中的下一个可用元素中cofee,例如

    ...
    /* while array not full, read each line in file and add to array */
    while (n < MAXCT && fgets (line, MAXC, fp)) {
        /* separate into name, color, world, pockets, size & VALIDATE return */
        if (sscanf (line, "%s %s %s %d %d", cofee[n].cofeeName, cofee[n].cofeeColor, 
                    cofee[n].cofeeWorld, &cofee[n].CofeePockets,
                    &cofee[n].onePocketSize) == 5) {
            n++;    /* increment cofeetype counter */
        }
    }
    ...

注意:您验证返回sscanf()以检查格式字符串中每个格式说明符的转换是否成功。格式字符串中有五个格式说明符,因此在考虑要使用来自的有效输入填充的变量之前检查一下。 "%s %s %s %d %d"return == 5line

至此,您已完成对输入的读取,因此,如果不读取stdin并输出存储在数组中的所有值,则只需关闭文件即可stdout,例如

    ...
    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);
    
    for (size_t i = 0; i < n; i++)                          /* output stored values */
        printf ("%-12s  %-12s  %-12s  %4d  %4d\n", cofee[i].cofeeName, cofee[i].cofeeColor,
                cofee[i].cofeeWorld, cofee[i].CofeePockets, cofee[i].onePocketSize);
}

剩下的就是以您选择的格式将值写入输出文件 - 这留给您。

总而言之(即上面的完整代码),您将拥有:

#include <stdio.h>

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

typedef struct cofeeshop {      /* typedef avoid writing 'struct name' in code */
    char cofeeName [MAXNM],
        cofeeColor [MAXNM],
        cofeeWorld [MAXNM];
    int CofeePockets,
        onePocketSize; 
} cofeeshop;

int main (int argc, char **argv) {
    
    char line[MAXC];                                        /* buffer to hold each line */
    cofeeshop cofee[MAXCT] = {{.cofeeName = ""}};           /* array of chofeeshop */
    size_t n = 0;                                           /* cofeetype counter */
    /* 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 array not full, read each line in file and add to array */
    while (n < MAXCT && fgets (line, MAXC, fp)) {
        /* separate into name, color, world, pockets, size & VALIDATE return */
        if (sscanf (line, "%s %s %s %d %d", cofee[n].cofeeName, cofee[n].cofeeColor, 
                    cofee[n].cofeeWorld, &cofee[n].CofeePockets,
                    &cofee[n].onePocketSize) == 5) {
            n++;    /* increment cofeetype counter */
        }
    }
    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);
    
    for (size_t i = 0; i < n; i++)                          /* output stored values */
        printf ("%-12s  %-12s  %-12s  %4d  %4d\n", cofee[i].cofeeName, cofee[i].cofeeColor,
                cofee[i].cofeeWorld, cofee[i].CofeePockets, cofee[i].onePocketSize);
}

编译

每次都在启用完整警告的情况下编译您的程序- 并且在没有警告的情况下编译之前不要接受代码,例如

gcc -Wall -Wextra -pedantic -Wshadow -std=c11 -Ofast -o bin/cofeeshop cofeeshop.c

-Wall -Wextra -pedantic启用 gcc/clang 的完整警告,添加-Wshadow以捕获任何隐藏的变量(例如i在代码中可能导致问题的两个不同范围内声明和使用)。对于 VS/W3用于完整警告。对于其他编译器,只需阅读选项文档以确定需要什么。

示例使用/输出

使用文件中的示例数据dat/cofeeshop.txt,您将拥有:

$ ./bin/cofeeshop dat/cofeeshop.txt
Lavazza       Gray          Europe         433    10
Machito       Black         Europe         433    10
Machito       White         Asia            24    18
Chiley        Black         Asia           198    17
Hucki         White         America         11    11

如果您想测试从 读取stdin,您可以将输入文件重定向到stdin,例如:

$ ./bin/cofeeshop < dat/cofeeshop.txt
Lavazza       Gray          Europe         433    10
Machito       Black         Europe         433    10
Machito       White         Asia            24    18
Chiley        Black         Asia           198    17
Hucki         White         America         11    11

如果您还有其他问题,请仔细查看并告诉我。

(注意:cofee通常拼写coffee- 但会与你所拥有的一起去:)

于 2020-12-12T02:57:34.180 回答