C从文本文件复制子字符串

时间:2018-01-16 00:44:38

标签: c substring text-files

说我有以下文本文件 -

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"。

2 个答案:

答案 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时,您的第一选择仍应是面向行的方法。

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