如何结合fscanf与fgets读取含带空格字符串和整数的文本文件?
解决带空格字段的行读取与数组存储问题
我明白你的困扰——带空格的学校名确实会让常规的fscanf操作卡壳,因为它默认以空格作为分隔符。结合fgets和字符串解析来处理这类格式是正确的思路,下面我一步步给你讲清楚怎么做:
核心思路
每行的结构是「带空格的学校名 + 无空格的学生名 + 数字ID」,直接用fscanf会把学校名拆成多个字段,所以我们先用fgets读取整行内容,再从行里拆分出三个字段,最后把数据存入结构体数组(这是存储这类关联数据最清晰的方式)。
完整实现代码
先定义一个结构体来存储每条学生记录,然后写读取和解析的逻辑:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> // 定义常量,限制各字段的最大长度和数组容量 #define MAX_SCHOOL_LEN 100 #define MAX_NAME_LEN 50 #define MAX_RECORDS 100 // 结构体存储单条学生信息 typedef struct { char school[MAX_SCHOOL_LEN]; char student_name[MAX_NAME_LEN]; int student_id; } StudentRecord; // 自定义字符串反转函数(避免依赖非标准的strrev) void reverse_string(char *str) { int len = strlen(str); for (int i = 0; i < len / 2; i++) { char temp = str[i]; str[i] = str[len - i - 1]; str[len - i - 1] = temp; } } int main() { StudentRecord records[MAX_RECORDS]; int record_count = 0; char line[256]; // 假设每行最多256个字符 // 逐行读取文件/输入 while (fgets(line, sizeof(line), stdin) != NULL && record_count < MAX_RECORDS) { // 第一步:去掉fgets读入的换行符 line[strcspn(line, "\n")] = '\0'; int line_len = strlen(line); if (line_len == 0) continue; // 跳过空行 // 第二步:拆分出学生名和ID(因为它们是无空格的末尾字段,反转后更容易提取) char reversed_line[256]; strcpy(reversed_line, line); reverse_string(reversed_line); char reversed_id[20], reversed_name[MAX_NAME_LEN]; if (sscanf(reversed_line, "%s %s", reversed_id, reversed_name) != 2) { // 格式不对,跳过该行 continue; } // 反转回来得到原始的ID和学生名 reverse_string(reversed_id); reverse_string(reversed_name); int student_id = atoi(reversed_id); // 第三步:提取学校名(原行去掉末尾的学生名、ID和分隔空格) int name_len = strlen(reversed_name); int id_len = strlen(reversed_id); int school_len = line_len - name_len - id_len - 2; // 减去两个空格的长度 if (school_len <= 0 || school_len >= MAX_SCHOOL_LEN) { // 学校名长度不符合要求,跳过 continue; } char school[MAX_SCHOOL_LEN]; strncpy(school, line, school_len); school[school_len] = '\0'; // 去掉学校名末尾可能存在的多余空格 while (school_len > 0 && isspace(school[school_len - 1])) { school_len--; school[school_len] = '\0'; } // 第四步:将数据存入数组 strncpy(records[record_count].school, school, MAX_SCHOOL_LEN - 1); records[record_count].school[MAX_SCHOOL_LEN - 1] = '\0'; // 确保字符串终止 strncpy(records[record_count].student_name, reversed_name, MAX_NAME_LEN - 1); records[record_count].student_name[MAX_NAME_LEN - 1] = '\0'; records[record_count].student_id = student_id; record_count++; } // 测试:打印读取到的所有记录 printf("读取到%d条记录:\n", record_count); for (int i = 0; i < record_count; i++) { printf("第%d条:学校=%s,学生=%s,ID=%d\n", i+1, records[i].school, records[i].student_name, records[i].student_id); } return 0; }
关键细节解释
为什么用字符串反转?
因为末尾的学生名和ID都是无空格的,反转后可以用sscanf轻松提取,避免在原行里复杂地查找空格位置,逻辑更简单可靠。处理换行符和边界情况
strcspn(line, "\n")会找到换行符的位置,直接替换成终止符,避免后续解析出错。- 加入了长度检查,防止字符串溢出,也跳过格式不正确的行。
- 去掉学校名末尾的多余空格,保证数据整洁。
为什么不用
fscanf直接读取?
如果混用fgets和fscanf,很容易出现缓冲区冲突(比如fscanf会留下换行符在缓冲区,导致下一次fgets读空行),统一用fgets读整行再解析是更稳妥的方案。
内容的提问来源于stack exchange,提问作者iLikePizza
相关产品推荐
相关产品推荐

