PHP如何读取文本文件第2行至末尾内容并将值存入数组
没问题,我来帮你搞定这个需求!你已经用fgets读取了第一行,接下来要读取从第2行到文件末尾的内容,把每行的逗号分隔值存入数组,我给你两种实用的实现方案,适配不同场景:
方案1:固定大小数组(适合已知数据规模)
如果你的文件行数、每行字段数都在可预估的范围内,用固定大小的多维数组就足够简单直接:
#include <stdio.h> #include <string.h> #include <stdlib.h> // 根据你的实际需求调整这些宏定义 #define MAX_LINE_LENGTH 1024 // 每行最大字符数 #define MAX_FIELDS_PER_LINE 20 // 每行最多字段数 #define MAX_ROWS 100 // 最多存储多少行(从第2行开始) int main() { FILE *fp = fopen("your_file.txt", "r"); if (fp == NULL) { perror("Failed to open file"); return 1; } // --- 你已经完成的第一行读取部分 --- char first_line[MAX_LINE_LENGTH]; if (fgets(first_line, sizeof(first_line), fp) == NULL) { fclose(fp); return 1; } printf("已读取第一行:%s", first_line); // 准备存储第2行及以后的数据 char data[MAX_ROWS][MAX_FIELDS_PER_LINE][MAX_LINE_LENGTH]; int row_count = 0; char line[MAX_LINE_LENGTH]; // 循环读取剩余所有行 while (fgets(line, sizeof(line), fp) != NULL) { // 先去掉行尾的换行符,避免干扰字段分割 line[strcspn(line, "\n")] = '\0'; // 用逗号分割当前行的字段 char *token = strtok(line, ","); int field_idx = 0; while (token != NULL && field_idx < MAX_FIELDS_PER_LINE) { // 把分割出的字段复制到数组中 strncpy(data[row_count][field_idx], token, MAX_LINE_LENGTH - 1); data[row_count][field_idx][MAX_LINE_LENGTH - 1] = '\0'; // 确保字符串终止 token = strtok(NULL, ","); // 继续分割下一个字段 field_idx++; } row_count++; if (row_count >= MAX_ROWS) { printf("警告:已达到最大行数限制,停止读取\n"); break; } } fclose(fp); // 验证读取结果 printf("\n第2行及以后的数据:\n"); for (int i = 0; i < row_count; i++) { printf("第%d行:", i + 2); for (int j = 0; j < MAX_FIELDS_PER_LINE; j++) { if (data[i][j][0] == '\0') break; // 遇到空字段就停止打印 printf("%s ", data[i][j]); } printf("\n"); } return 0; }
关键细节说明:
- 处理换行符:用
strcspn快速定位换行符位置并替换为\0,避免把换行符当成字段的一部分。 - 字符串分割:
strtok是C标准库中分割字符串的常用工具,第一次传入整行内容,后续传入NULL即可继续分割同一行的剩余部分。注意:strtok会修改原字符串,如果需要保留原始行内容,记得先复制一份。
方案2:动态内存分配(适合数据规模不确定)
如果文件的行数或字段数无法预估,用动态内存分配更灵活,不会浪费空间也不会限制数据量:
#include <stdio.h> #include <string.h> #include <stdlib.h> #define MAX_LINE_LENGTH 1024 #define INITIAL_ROWS 10 // 初始分配的行数 int main() { FILE *fp = fopen("your_file.txt", "r"); if (fp == NULL) { perror("Failed to open file"); return 1; } // --- 你已经完成的第一行读取部分 --- char first_line[MAX_LINE_LENGTH]; if (fgets(first_line, sizeof(first_line), fp) == NULL) { fclose(fp); return 1; } // 动态存储结构:data是行数组,每行是字段数组,每个字段是字符串 char ***data = malloc(INITIAL_ROWS * sizeof(char **)); if (data == NULL) { perror("malloc failed"); fclose(fp); return 1; } int current_max_rows = INITIAL_ROWS; int row_count = 0; char line[MAX_LINE_LENGTH]; while (fgets(line, sizeof(line), fp) != NULL) { line[strcspn(line, "\n")] = '\0'; // 先统计当前行的字段数量(方便分配内存) char *temp_line = strdup(line); if (temp_line == NULL) { perror("strdup failed"); break; } int field_count = 0; char *token = strtok(temp_line, ","); while (token != NULL) { field_count++; token = strtok(NULL, ","); } free(temp_line); // 如果当前行数超过分配的内存,扩展行数组 if (row_count >= current_max_rows) { current_max_rows *= 2; // 每次扩容一倍 char ***temp_data = realloc(data, current_max_rows * sizeof(char **)); if (temp_data == NULL) { perror("realloc failed"); break; } data = temp_data; } // 为当前行分配字段数组的内存 data[row_count] = malloc(field_count * sizeof(char *)); if (data[row_count] == NULL) { perror("malloc failed"); break; } // 分割并存储每个字段 token = strtok(line, ","); int field_idx = 0; while (token != NULL) { data[row_count][field_idx] = strdup(token); if (data[row_count][field_idx] == NULL) { perror("strdup failed"); // 清理已分配的内存,避免泄漏 for (int i = 0; i < field_idx; i++) { free(data[row_count][i]); } free(data[row_count]); break; } field_idx++; token = strtok(NULL, ","); } row_count++; } fclose(fp); // 打印验证结果 printf("\n动态存储的第2行及以后数据:\n"); for (int i = 0; i < row_count; i++) { printf("第%d行:", i + 2); int j = 0; while (data[i][j] != NULL) { printf("%s ", data[i][j]); j++; } printf("\n"); } // 释放所有动态分配的内存(必须做,避免内存泄漏) for (int i = 0; i < row_count; i++) { int j = 0; while (data[i][j] != NULL) { free(data[i][j]); j++; } free(data[i]); } free(data); return 0; }
关键细节说明:
- 动态扩容:当行数超过初始分配的内存时,用
realloc把数组容量翻倍,保证不会溢出。 - 内存清理:动态分配的内存必须手动释放,否则会造成内存泄漏,代码最后部分的内存释放逻辑一定要加上。
- 字段统计:先复制一行内容并统计字段数,再对应分配内存,避免浪费空间。
额外提示:
如果你的字段中包含带逗号的内容(比如CSV文件中带引号的字段,如"张三,男",25),strtok就无法正确分割了,这时候需要手动解析字符串,判断引号的包裹情况。不过如果你的场景是简单的逗号分隔,上面的方案完全够用。
内容的提问来源于stack exchange,提问作者dragi
相关产品推荐
相关产品推荐

