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

c - reading and altering string from csv file

所以我在 excel 中打开了一个 csv 文件,我正在尝试复制该文件中的所有内容并将其写入一个新文件并进行修改。我在第 4 列的每一行中都填充了一种特定的食物,我想对其进行更改,以便它只打印出新文件中每种食物的第一个字母。像 'Meatballs' 应该打印为 '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);
    } 

获取字段函数:

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

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

您的问题与 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

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

由于您没有 returning line 并且您在 getfield() 中操作原始指针的副本,因此调用者对 tmp 的更改是在第一次调用 getfield() 后丢失,分隔符已被 空字符 覆盖,并且当 时您对 getfield() 的下一次调用失败遇到空字符 而不是定界符。

与@Seamus 略有不同,我会在 getfield() 和 return 中复制要操作的字符串副本,并在分配给副本的内存块中分配令牌副本原始字符串不变。这也给调用者带来了释放分配给令牌的内存的负担。这将允许在您想要的逗号分隔行内获得任意数量的随机字段。如果您按顺序处理字段,那么只需跟踪已处理的标记数即可提高效率。

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

在调用获取第一个字段并将其与 "Food" 进行比较后,您需要做的就是获取第四个字段,然后简单地取消引用指针 returned 的顺序获取第一个字符。您无需将每个第四字段与例如"Meatballs""Icecream" 在获取第一个字符之前。那是不必要的。

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

#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() 是必需的,因为 cpytok 重叠)

有多种方法可以解决这个问题。只要完成工作并且相当高效,并且释放他们分配的所有内存,一切都很好。检查一下,如果您还有其他问题,请告诉我。