您使用
fgets
处理文件I/O,因为它提供了一种更灵活、更健壮的方式来读取、验证和准备解析所读取的数据行。通常建议这样做
面向生产线
输入(来自文件或用户)。但是,这是将多个记录视为
已格式化
输入确实有一些优势。
让我们从读取数据文件并捕获
name:....
和
address:...
简单数据结构中的数据,用于在
20-char
每个的数组。读取每一行,验证长度,尾部
'\n'
已删除,然后
strchr
用于定位
':'
排队等候。(我们不关心没有
':'
). 之前的标签
':'
复制到
tmp
然后与
"name"
或
"address"
确定要读取的值。一旦读取地址数据,两个
name
和
addr
值打印到
stdout
,
#include <stdio.h>
#include <string.h>
enum { MAXC = 20, MAXS = 256 };
typedef struct {
char name[MAXC],
addr[MAXC];
} data;
int main (int argc, char **argv) {
char buf[MAXS] = "",
*name = "name", /* name/address literals for comparison */
*addr = "address";
data mydata = { .name = "" };
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXS, fp)) { /* read each line */
char *p = buf, /* pointer to use with strchr */
tmp[MAXC] = ""; /* storage for labels */
size_t len = strlen (buf); /* get buf len */
if (len && buf[len - 1] == '\n') /* validate last char is '\n' */
buf[--len] = 0; /* overwrite with nul-character */
else if (len + 1 == MAXS) { /* handle string too long */
fprintf (stderr, "error: line too long or no '\n'\n");
return 1;
}
if ((p = strchr (buf, ':'))) { /* find ':' in buf */
size_t labellen = p - buf, /* get length of label */
datalen = strlen (p + 1); /* get length of data */
if (labellen + 1 > MAXC) { /* validate both lengths */
fprintf (stderr, "error: label exceeds '%d' chars.\n", MAXC);
return 1;
}
if (datalen + 1 > MAXC) {
fprintf (stderr, "error: data exceeds '%d' chars.\n", MAXC);
return 1;
}
strncpy (tmp, buf, labellen); /* copy label to temp */
tmp[labellen] = 0; /* nul-terminate */
if (strcmp (name, tmp) == 0) /* is the label "name" ? */
strcpy (mydata.name, p + 1);
else if (strcmp (addr, tmp) == 0) { /* is the label "address" ? */
strcpy (mydata.addr, p + 1);
/* record complete -- output results */
printf ("\nname : %s\naddr : %s\n", mydata.name, mydata.addr);
}
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
(
注:
有很多方法可以构建这种逻辑。上述示例仅代表一种半标准方法)
使用/输出示例
$./bin/nameaddr <dat/nameaddr.txt
name : asdfg
addr : zcvxz
name : qwerwer
addr : zxcvzxcvxz
我很难让你相信
fgets
是解决这个问题的方法。为什么?这里我们基本上是在阅读
已格式化
由3行数据组成的输入。这个
设置字符串格式
对于
fscanf
不在乎涉及多少行,可以轻松构造为跳过
“\n”
在格式化输入中。这可以为正确的输入文件提供(一个更脆弱但有吸引力的选择)。
例如,上面的代码可以使用
fscanf公司
对于格式化读取:
#include <stdio.h>
#define MAXC 20
typedef struct {
char name[MAXC],
addr[MAXC];
} data;
int main (int argc, char **argv) {
data mydata = { .name = "" };
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* read 3-lines at a time separating name and address at once */
while (fscanf (fp, " name:%19s address:%19s ,",
mydata.name, mydata.addr) == 2)
printf ("\nname : %s\naddr : %s\n", mydata.name, mydata.addr);
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
(输出相同)
在极少数情况下,对于正确的数据文件,
fscanf公司
可以为
面向生产线
阅读时使用
fgets
. 然而,您的第一选择应该仍然是
使用以下方法之一
fgets
或POSIX
getline
.
如果你还有其他问题,请仔细查看并告诉我。