如果您想要一个简短的示例来帮助您入门,那么在将字符串读入字符数组(缓冲区)时首先要记住的是不要吝啬缓冲区大小!。使用10 和20 可能会减少您显示的一小部分数据,但是当您尝试阅读Mandheling Black 时会发生什么?使用您预期最长的字符串,并且在使用固定数组时至少将字符数加倍(如果它是一个要重复使用的缓冲区,例如读取每一行,1K 缓冲区大小就可以了,2K 也可以)
尽量避免使用 MagicNumbers,例如 10 和 20,而是:
#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;
...
如果您在上面注意到,struct cofeeshop 中的 typedef 将以名称 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() 的返回值以检查 format-string 中每个 format-specifier 的转换@ 987654345@ 成功。 format-string 中有五个 format-specifiers,因此在考虑使用来自 line 的有效输入填充变量之前,请检查 return == 5。
此时您已完成对输入的读取,因此如果未从 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——但会与您所拥有的 :) 一起使用