簡體   English   中英

從C中的文本文件讀取CSV

[英]Reading CSV from text file in C

我正在嘗試從C文本文件中讀取CSV。文本文件格式為

1,Bob,bob@gmail.com
2,Daniel,daniel@gmail.com
3,John,john@gmail.com

當我運行該程序時,數字顯示正常,但是名稱和電子郵件顯示為垃圾。 這是我的程序...

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

typedef struct {
    int number;
    char* name;
    char* email;
} Owner;

Owner owners[100];

int load(char* filename)
{
    char buffer[200];
    char token[50];
    Owner* owner;
    int owners_size = 0;
    FILE* file = fopen(filename, "r");

    while(fgets(buffer, 200, file) != NULL)
    {
        owner = (Owner*)malloc(sizeof(Owner));
        owner->number = atoi(strtok(buffer, ","));
        owner->name = strtok(NULL, ",");
        owner->email = strtok(NULL, ",");
        owners[owners_size++] = *owner;
    }

    fclose(file);
    return owners_size;
}

int main()
{
    int choise, owners_size, index;
    char* owners_filename = "owners2.txt";

    owners_size = load(owners_filename);

    if(owners_size)
    {
        printf("owners size: %d\n\n", owners_size);

        for(index = 0; index < owners_size; index++)
            printf("%d, %s %s\n", owners[index].number, owners[index].name, owners[index].email);
    }
}

誰能告訴我原因是什么。 我感謝您的幫助。

兩個問題:

  1. 您沒有為結構中的字符串分配空間:

     typedef struct { int number; char *name; char *email; } Owner; 

    您需要為這些指針提供空間以指向它們以容納名稱。

  2. 您繼續提供指向緩沖區的指針,該緩沖區可在輸入的每一行中重復使用:

     while(fgets(buffer, 200, file) != NULL) { owner = (Owner*)malloc(sizeof(Owner)); owner->number = atoi(strtok(buffer, ",")); owner->name = strtok(NULL, ","); owner->email = strtok(NULL, ","); owners[owners_size++] = *owner; } 

    第一行作為一些指針存儲在緩沖區中。 然后,下一行將覆蓋緩沖區,並再次將該行切掉,從而遍歷原始輸入。

考慮使用strdup()

while (fgets(buffer, 200, file) != NULL)
{
    owner = (Owner *)malloc(sizeof(Owner));
    owner->number = atoi(strtok(buffer, ","));
    owner->name = strdup(strtok(NULL, ","));
    owner->email = strdup(strtok(NULL, ","));
    owners[owners_size++] = *owner;
}

這是有點危險的代碼(我不會在生產代碼中使用它),因為它不會檢查strtok()在期望時找到了令牌(或strdup()是否成功)。 同樣,我也不會在生產代碼中使用strtok() 如果可能,我會使用POSIX strtok_r()或Microsoft的strtok_s() ,或者使用其他替代技術,可能使用strspn()strcspn() 如果strdup()不可用,則可以使用相同或不同的名稱編寫自己的名稱:

char *strdup(const char *str)
{
    size_t len = strlen(str) + 1;
    char *dup = malloc(len);
    if (dup != 0)
        memmove(dup, str, len);  // Or memcpy() - that is safe in this context
    return(dup);
}

您可能會注意到,您的代碼僅適用於簡單的CSV文件。 如果遇到這樣的行(這是合法的CSV),則會遇到問題(值中帶有引號,並且由於帶引號的字符串中的逗號而導致拆分錯誤):

1,"Bob ""The King"" King","Bob King, Itinerant Programmer <bob@gmail.com>"

strtok()返回的指針指向它正在解析的緩沖區內的地址,在這種情況下為局部變量buffer load()返回變量時,它不在范圍內(即使不是所有owners實例都指向相同的地址)。 您需要復制strtok()返回的字符串。 您可以使用strdup()如果可用)或使用malloc()strcpy()

不需要malloc()新的Owner實例,因為它們的數組已經存在(原樣的代碼存在內存泄漏)。

請注意,沒有防止超出owners數組范圍的保護措施。 如果文件有100條目,則循環將超出數組的范圍。 延長while的終止條件以防止這種情況:

while(owners_size < sizeof(owners) / sizeof(owners[0]) &&
      fgets(buffer, 200, file) != NULL)
{
}

您只是將指針存儲在本地緩沖區中。 當您離開load()buffer已消失,不再可用。

您必須先為nameemail分配內存,然后才能將其復制到“ Owner結構中。

char *tok;
tok = strtok(NULL, ",");
len = strlen(tok);
owner->name = malloc(len + 1);
strcpy(owner->name, tok);
...

[編輯:您需要分配len+1個字節,以便為NUL字符NUL空間。 -扎克]

您只有一個行緩沖區。 load循環中的每個循環都會掩蓋上一個循環中的文本。 如果這還不夠糟糕,則在load返回時會破壞緩沖區。

快速的解決方法是改變

owner->name = strtok(NULL, ",");
owner->email = strtok(NULL, ",");

owner->name = strdup(strtok(NULL, ","));
owner->email = strdup(strtok(NULL, ","));

(如果您沒有strdup 得到一台真正的電腦 這很簡單。)

但是,如果我正在查看您的代碼,我將為您提供固定大小的行緩沖區,固定大小的所有者數組,內存泄漏,使用atoi而不是strtol ,使用strtok而不是strsep以及缺少引號處理並解析錯誤恢復,並指出將每行分配為一個單元,然后將指針保存到其中會更有效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM