c-从csv文件读取和更改字符串

时间:2019-12-05 02:36:05

标签: c fopen fwrite fread strcmp

所以我在excel中打开了一个csv文件,我正在尝试复制该文件中的所有内容并将其写入更改后的新文件中。我有第四列,每行中都填充有特定食物,我想对其进行更改,以便仅在新文件中打印出每种食物的首字母。像“丸子”一样,应打印为“ M”。问题是我不断遇到细分错误,但不知道是什么原因引起的。我花了几个小时试图解决这个简单的问题,但没有成功。任何帮助,将不胜感激。

    char readLine[lineCount];
    while(fgets(readLine, lineCount, fd)) {
        char* tmp = strdup(readLine);

        if(strcmp(getfield(tmp, 1), "Food") == 0){

            if(strcmp(getfield(tmp, 4), "Meatballs") == 0){
                fprintf(ft, "%c,", 'M');

            }else if(strcmp(getfield(tmp, 4), "Icecream") == 0){
                fprintf(ft, "%c,", 'I');
            }   

        }

        fprintf(ft, "\n");
        free(tmp);
    } 

getfield函数:

const char* getfield(char* line, int num){
    const char* tok;

    while(tok = strsep(&line, ","))
    {   
        if(!--num) {
            printf("%s\n", tok);
            return tok;
        }
    }
    return NULL;
}

2 个答案:

答案 0 :(得分:2)

您的问题在于strsep函数。 strsep对传递给它的char**进行突变,以便在每次调用时都返回令牌,而char**包含字符串的其余部分减去令牌和分隔符。解决方案是不使用strsep或在调用之前复制字符串

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const char* getfield(const char *line, int num){
    const char* tok;
    char *dup_line, *tofree;
    dup_line = tofree = strdup(line);
    while(tok = strsep(&dup_line, ","))
    {
        if(!--num){
            free(tofree);
            return tok;
        }
    }
    free(tofree);
    return NULL;
}

int main(int argc, char *argv[])
{
    char *record = "foo,bar,foobar,barfoo";
    printf("%s\n", getfield(record, 3));
    printf("%s\n", getfield(record, 1));
}

打印foobar\nfoo\n

答案 1 :(得分:2)

@Seamus的解释完全正确。当您将tmp传递给例如getfield(tmp, 1)getfield()tmp中收到指针line的副本,该副本指向tmp的原始地址,但拥有一个自己的地址。然后在getfield()中调用while(tok = strsep(&line, ",")),它修改了line指向的字符串,并写了一个 nul-character 来代替每个定界符,并更新了line指向最后一个定界符,以便可以解析下一个标记。

由于您没有返回line,并且对getfield()中的原始指针进行了操作,因此在第一次调用{之后,调用者对tmp所做的更改将丢失{1}},分隔符已被 nul-character 覆盖,并且当遇到 nul-character 而不是a时,您对getfield()的下一次调用失败定界符。

与@Seamus稍有不同,我将在getfield()中对字符串进行复制,然后在分配给该副本的内存块内返回令牌的已分配副本,而使原始字符串保持不变。这也给调用者带来了负担,以释放分配给令牌的内存。但是,这将允许您在所需的逗号分隔行中获得许多随机字段。如果按顺序处理这些字段,那么只需跟踪已处理的令牌数量,就可以提高效率。

理想情况下,您只需将读取所有标记的行标记为一个已分配的指针数组,就可以通过一次调用标记化函数将其返回给调用方。

在调用获取第一字段并将其与getfield()进行比较之后,您要做的就是获取第四字段,然后简单地取消引用返回的指针以获取第一字符。您无需将每个第四字段都与之进行比较。 "Food""Meatballs",然后再获取第一个字符。没必要。

在将csv文件名作为第一个参数的简短示例中,将其完全放入(或者如果没有给出参数,则从"Icecream"读取),可以执行以下操作:

stdin

注意: {{1}是必需的,因为#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXC 1024 /* returns allocated token for field on success, NULL otherwise */ char *getfield (char *buf, size_t field) { size_t len = strlen(buf); /* size of input string */ char *cpy = malloc (len + 1), /* allocate for copy */ *p, /* pointer to use with strsep */ *tok = NULL; /* token for requested field */ if (!cpy) /* validate allocation */ return NULL; memcpy (cpy, buf, len + 1); /* copy buf to cpy */ p = cpy; /* pointer to cpy, preserves cpy address */ while (field-- && (tok = strsep (&p, ","))) {} /* get field field */ /* copy tok to cpy and return cpy on success or NULL on failure */ return tok ? memmove (cpy, tok, strlen(tok) + 1) : NULL; } int main (int argc, char **argv) { char buf[MAXC]; /* 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 (fgets (buf, MAXC, fp)) { char *first = getfield (buf, 1); char *fourth = getfield (buf, 4); if (first && fourth && strcmp (first, "Food") == 0) printf ("%c\n", *fourth); free (first); free (fourth); } if (fp != stdin) /* close file if not stdin */ fclose (fp); return 0; } memmove()重叠)

有多种方法可以解决此问题。只要能做到,一切都很好,并且效率相当高,并释放他们分配的所有内存。仔细研究一下,如果您还有其他问题,请告诉我。