C语言新手求助:创建存储未知行数文件数据的二维数组
嘿,作为C语言新手碰到这种动态数据存储的问题太正常啦!我来一步步帮你理清楚怎么搞定这个需求~
首先得说一句:用结构体数组来存储这些数据会比二维数组合适得多,因为你的数据包含不同类型(字符串、整数、浮点数),二维数组很难兼顾不同类型的存储——如果硬要用二维数组,只能把所有数据存成字符串,后续处理会非常麻烦。所以我先给你讲推荐的结构体方案,最后再提二维数组的实现方式。
推荐方案:使用结构体数组
1. 定义结构体
先定义一个结构体来对应每行的4个字段,这样每个字段的类型都能匹配:
#include <stdio.h> #include <stdlib.h> // 定义存储每个人数据的结构体 typedef struct { char role[20]; // 存储student/teacher,长度根据实际情况调整 char name[50]; // 存储唯一姓名 int age; // 年龄(整数) float weight; // 体重(浮点数) } Person;
2. 统计文件行数(提前分配内存)
因为不知道总行数,我们可以先遍历一次文件统计行数,再分配对应大小的内存:
// 辅助函数:统计文件行数 int count_lines(FILE *fp) { int count = 0; char buffer[256]; // 逐行读取直到文件末尾 while (fgets(buffer, sizeof(buffer), fp) != NULL) { count++; } // 把文件指针移回开头,方便后续读取数据 fseek(fp, 0, SEEK_SET); return count; } int main() { // 打开输入文件 FILE *fp = fopen("input.txt", "r"); if (fp == NULL) { perror("Failed to open input.txt"); // 打印错误原因 return 1; } // 统计行数 int total_people = count_lines(fp); // 分配结构体数组的内存 Person *people = (Person*)malloc(total_people * sizeof(Person)); if (people == NULL) { perror("Failed to allocate memory"); fclose(fp); return 1; } // 逐行读取数据到结构体数组 for (int i = 0; i < total_people; i++) { // 假设每行用空格分隔数据,格式要和文件匹配 fscanf(fp, "%s %s %d %f", people[i].role, people[i].name, &people[i].age, &people[i].weight); } // 示例:打印读取到的数据 printf("Successfully read %d records:\n", total_people); for (int i = 0; i < total_people; i++) { printf("Role: %s | Name: %s | Age: %d | Weight: %.1f\n", people[i].role, people[i].name, people[i].age, people[i].weight); } // 一定要释放内存+关闭文件,避免泄漏 free(people); fclose(fp); return 0; }
进阶版:动态扩容(无需提前统计行数)
如果文件特别大,或者不想先遍历一次统计行数,可以用realloc边读边扩容内存,这样更灵活:
int main() { FILE *fp = fopen("input.txt", "r"); if (fp == NULL) { perror("Failed to open input.txt"); return 1; } int capacity = 4; // 初始容量,先存4条数据 Person *people = (Person*)malloc(capacity * sizeof(Person)); if (people == NULL) { perror("Failed to allocate memory"); fclose(fp); return 1; } int current_count = 0; Person temp_person; // 逐行读取,直到文件结束 while (fscanf(fp, "%s %s %d %f", temp_person.role, temp_person.name, &temp_person.age, &temp_person.weight) == 4) { // 如果当前数据量达到容量,就扩容(比如翻倍) if (current_count == capacity) { capacity *= 2; Person *temp_ptr = (Person*)realloc(people, capacity * sizeof(Person)); if (temp_ptr == NULL) { perror("Failed to reallocate memory"); free(people); fclose(fp); return 1; } people = temp_ptr; } // 把临时数据存入数组 people[current_count++] = temp_person; } // 打印结果 printf("Successfully read %d records:\n", current_count); for (int i = 0; i < current_count; i++) { printf("Role: %s | Name: %s | Age: %d | Weight: %.1f\n", people[i].role, people[i].name, people[i].age, people[i].weight); } free(people); fclose(fp); return 0; }
如果你非要用二维数组(不推荐)
因为二维数组要求所有元素类型相同,所以只能把所有字段都存成字符串。这种方式内存管理更复杂,后续处理数值类型还要手动转换,仅作参考:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_FIELDS 4 // 每行4列 #define MAX_FIELD_LEN 50 // 每个字段的最大长度 int main() { FILE *fp = fopen("input.txt", "r"); if (fp == NULL) { perror("Failed to open input.txt"); return 1; } // 先统计行数 int line_count = 0; char buffer[256]; while (fgets(buffer, sizeof(buffer), fp) != NULL) line_count++; fseek(fp, 0, SEEK_SET); // 分配二维指针数组:data[行][列] char ***data = (char***)malloc(line_count * sizeof(char**)); if (data == NULL) { perror("Failed to allocate memory"); fclose(fp); return 1; } // 为每行分配字段指针 for (int i = 0; i < line_count; i++) { data[i] = (char**)malloc(MAX_FIELDS * sizeof(char*)); if (data[i] == NULL) { // 内存分配失败时,要释放之前已分配的内存,避免泄漏 for (int j = 0; j < i; j++) { for (int k = 0; k < MAX_FIELDS; k++) free(data[j][k]); free(data[j]); } free(data); fclose(fp); return 1; } // 为每个字段分配内存并读取数据 for (int j = 0; j < MAX_FIELDS; j++) { data[i][j] = (char*)malloc(MAX_FIELD_LEN * sizeof(char)); if (data[i][j] == NULL) { // 同样要释放已分配的内存 for (int x = 0; x <= i; x++) { for (int y = 0; y < j; y++) free(data[x][y]); free(data[x]); } free(data); fclose(fp); return 1; } fscanf(fp, "%s", data[i][j]); } } // 打印数据 printf("Successfully read %d records:\n", line_count); for (int i = 0; i < line_count; i++) { printf("Role: %s | Name: %s | Age: %s | Weight: %s\n", data[i][0], data[i][1], data[i][2], data[i][3]); } // 逐层释放内存 for (int i = 0; i < line_count; i++) { for (int j = 0; j < MAX_FIELDS; j++) free(data[i][j]); free(data[i]); } free(data); fclose(fp); return 0; }
额外注意事项
- 如果你的姓名包含空格(比如
Mary Ann),fscanf会把空格当成分隔符,导致读取错误。这时候可以用fgets读取整行,再用strtok分割:char buffer[256]; while (fgets(buffer, sizeof(buffer), fp) != NULL) { // 去掉换行符 buffer[strcspn(buffer, "\n")] = '\0'; // 按空格分割字符串 char *token = strtok(buffer, " "); strcpy(temp_person.role, token); token = strtok(NULL, " "); strcpy(temp_person.name, token); token = strtok(NULL, " "); temp_person.age = atoi(token); token = strtok(NULL, " "); temp_person.weight = atof(token); // 存入数组... } - 所有内存分配后都要检查是否成功,避免空指针访问导致程序崩溃
- 使用完动态分配的内存一定要释放,避免内存泄漏
内容的提问来源于stack exchange,提问作者gokhan_ozeloglu
相关产品推荐
相关产品推荐

