You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何结合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;
}

关键细节解释

  1. 为什么用字符串反转?
    因为末尾的学生名和ID都是无空格的,反转后可以用sscanf轻松提取,避免在原行里复杂地查找空格位置,逻辑更简单可靠。

  2. 处理换行符和边界情况

    • strcspn(line, "\n")会找到换行符的位置,直接替换成终止符,避免后续解析出错。
    • 加入了长度检查,防止字符串溢出,也跳过格式不正确的行。
    • 去掉学校名末尾的多余空格,保证数据整洁。
  3. 为什么不用fscanf直接读取?
    如果混用fgets和fscanf,很容易出现缓冲区冲突(比如fscanf会留下换行符在缓冲区,导致下一次fgets读空行),统一用fgets读整行再解析是更稳妥的方案。

内容的提问来源于stack exchange,提问作者iLikePizza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:19:18