说我有以下文本文件 -
name:asdfg
address:zcvxz
,
name:qwerwer
address:zxcvzxcvxz
,
我想将名称(没有"名称:")复制到某个字符串变量,将地址复制到另一个字符串变量等等。
如何在不破坏内存的情况下这样做? 尝试使用(示例) -
char buf[50];
while (fgets(buf, 50, file) != NULL) {
if (!strncmp(buf, "name", 4))
strncpy(somestring, buf + 5, 20)
//do the same for address, continue looping
但是文本行的长度不同,所以它似乎从缓冲区中复制了所有类型的废话,因为字符串不会被终止,因此它会复制" asdfg crapcrapcrap"。
答案 0 :(得分:0)
如果名称是20个字符或更长,strncpy()
将不会将空终止符复制到目标字符串,因此您需要自己添加它。
strncpy(somestring, buf + 5, 19);
somestring[19] = '\0';
答案 1 :(得分:0)
使用fgets
来处理文件I / O值得称赞,因为它提供了一种更灵活,更健壮的方式来读取,验证和准备解析您读取的数据行。通常建议使用面向行的输入(来自文件或来自用户)。但是,这是将多个记录视为格式化输入的情况之一确实具有一些优势。
让我们从一个示例开始,读取您的数据文件并在一个简单的数据结构中捕获name:....
和address:...
数据,以保存{{1}中的名称和地址数据值每个数组。读取每一行,验证长度,删除尾随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
是解决这个问题的方法。为什么?这里我们基本上读取由3行数据组成的格式化输入。 fgets
的格式字符串并不关心涉及多少行,并且可以轻松构建以跳过格式化输入中的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
或POSIX fgets
时,您的第一选择仍应是面向行的方法。
仔细看看,如果您有其他问题,请告诉我。