将逗号分隔的文本文件读入Struct c



我有一个逗号分隔的船及其规格列表,我需要将其读入结构体。每行包含一艘不同的船以及它们的规格,所以我必须逐行阅读文件。

示例输入文件(我将使用的文件有超过20行):

pontoon,Crest,Carribean RS 230 SLC,1,Suzuki,115,Blue,26,134595.00,135945.00,1,200,0,250,450,450,0
fishing,Key West,239 FS,1,Mercury,250,Orange,24,86430.00,87630.00,0,0,250,200,500,250,0
sport boat,Tahoe,T16,1,Yamaha,300,Yellow,22,26895.00,27745.00,0,250,0,0,350,250,0

我有一个链表watercraft_t:

typedef struct watercraft {
char type[15];     // e.g. pontoon, sport boat, sailboat, fishing, 
//      canoe, kayak, jetski, etc.
char make[20];
char model[30];
int propulsion;    // 0 = none; 1 = outBoard; 2 = inBoard; 
char engine[15];   // Suzuki, Yamaha, etc.
int hp;             // horse power  
char color[25];
int length;        // feet
double base_price;
double total_price;
accessories_t extras;
struct watercraft *next;
} watercraft_t;

main函数打开文件并将其存储在一个指针中:

FILE * fp = fopen(argv[1], "r"); // Opens file got from command line arg

然后将该文件传递给一个函数,该函数应该准确解析1行,然后返回该节点以放置在链表中。

// Create watercrafts from the info in file
watercraft_t *new_waterCraft( FILE *inFile )
{
watercraft_t *newNode;
newNode = (watercraft_t*)malloc(sizeof(watercraft_t));
fscanf(inFile, "%s %s %s %d %s %d %s %d %lf %lf", newNode->type, newNode->make, newNode->model, &(newNode->propulsion), newNode->engine, &(newNode->hp), newNode->color, &(newNode->length), &(newNode->base_price), &(newNode->total_price));
return newNode;
}

当调用一个函数来打印每艘船的类型时,结果如下:

1. pontoon,Crest,CRS
2. SLC,1,Suzuki,11fishing,Key
3. SLC,1,Suzuki,11fishing,Key
4. SLC,1,Suzuki,11fishing,Key
5. SLC,1,Suzuki,11fishing,Key
6. SLC,1,Suzuki,11fishing,Key
7. SLC,1,Suzuki,11fishing,Key
8. SLC,1,Suzuki,11fishing,Key
9. SLC,1,Suzuki,11fishing,Key
10. SLC,1,Suzuki,11fishing,Key
11. SLC,1,Suzuki,11fishing,Key
12. SLC,1,Suzuki,11fishing,Key
13. SLC,1,Suzuki,11fishing,Key
14. SLC,1,Suzuki,11fishing,Key
15. SLC,1,Suzuki,11fishing,Key
16. SLC,1,Suzuki,11fishing,Key
17. SLC,1,Suzuki,11fishing,Key

我已经将问题缩小到如何使用fscanf从文件中读取值。

我尝试的第一件事是在所有占位符之间使用%*c,但是运行之后,我的输出看起来完全一样。我意识到的下一件事是,我将无法使用fscanf,因为文本文件将有需要读取的空白。

我的下一个想法是使用fgets,但我认为我也不能使用它,因为我不确定每次需要读取多少字符。我只需要它在行尾停止读取,同时用逗号分隔值。

我已经寻找了几个小时的答案,但到目前为止似乎没有任何效果。

当您使用%s时,文本将被解析,直到找到空格或换行字符,例如,对于文件的第一行,fscanf将将"pontoon,Crest,Carribean"存储在make中,当发现空格时解析停止。

fscanf说明符必须包含文件中的行,包括逗号,所以您需要这样做:

" %14[^,], %19[^,], %29[^,], %d , %14[^,], %d , %24[^,], %d , %lf , %lf /*...*/"

(注意格式说明符开头的空格,这可以避免解析以前读取的剩余空白)

格式说明符[^,]使fscanf读取,直到找到逗号或达到限制大小,它还将解析与%s相反的空格,此外,使用%14[^,]避免了通过缓冲区溢出的潜在未定义行为,因为它将读取限制为14字符加上与缓冲区大小匹配的空终止符15

使用fgets来解析行似乎是一个好主意,然后您可以使用sscanf来转换值,它的工作原理类似于fscanf

我建议您验证*scanf的返回,以确保读取了正确的字段数。

我的下一个想法是使用fgets,但我不认为我将能够使用,因为我不确定有多少字符将不得不每次读取。我只需要它在行尾停止读取,同时用逗号分隔值。

这个方法不错。可以这样做:

int main(void) {
FILE *fp = fopen("in.txt", "r");
while(1) {
int length = 0;
int ch;
long offset= ftell(fp);
// Calculate length of next line
while((ch = fgetc(fp)) != 'n' && ch != EOF)
length++;

// Go back to beginning of line
fseek(fp, offset, SEEK_SET);
// If EOF, it's the last line, and if the length is zero, we're done
if(ch == EOF && length == 0)
break;

// Allocate space for line plus BOTH n AND 
char *buffer = malloc(length+2);
// Read the line
fgets(buffer, length+2, fp);
// Do something with the buffer, for instance with sscanf
// Cleanup
free(buffer);
if(ch == EOF) break;
}
}

我省略了所有的错误检查,以保持代码的简短。

我不推荐使用fscanf的方法。容易出错,不灵活。

下面是解析csv文件的示例:

int main(int argc, char *argv[])
{
#define LINE_MAX 1024
char line_buf[LINE_MAX];
char *line = line_buf;
char *delim;
int sep = ',';
#define FIELD_MAX 128
char field[FIELD_MAX];
FILE *fp = fopen(argv[1], "r");
//foreach line
while ((line = fgets(line_buf, LINE_MAX, fp))) {
//iterate over the line
for (char *line_end = line + strlen(line) - 1; line < line_end;) {
//search for a separator
delim = strchr(line, sep);
//strchr returns NULL if no separator was found
if (delim == NULL) {
//we set delim to the end of line,
//because we want to process the remaining chars (field)
delim = line_end;
}
//the first character of field is at 'line'
//the last character of field is at delim - 1
//delim points to the separator
//e.g.
size_t len = delim - line;
memcpy(field, line, len);
field[len] = '';
printf("%s ", field);
//end of example
//set the position to the next character after the separator
line = delim + 1;
}
printf("n");
}
return EXIT_SUCCESS;
}

注意: csv文件末尾添加空行,否则将不考虑最后一行的最后一个字符(原因:line_end = line + strlen(line) - 1)。

相关内容

最新更新