[英]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);
}
}
誰能告訴我原因是什么。 我感謝您的幫助。
兩個問題:
您沒有為結構中的字符串分配空間:
typedef struct { int number; char *name; char *email; } Owner;
您需要為這些指針提供空間以指向它們以容納名稱。
您繼續提供指向緩沖區的指針,該緩沖區可在輸入的每一行中重復使用:
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
已消失,不再可用。
您必須先為name
和email
分配內存,然后才能將其復制到“ 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.