文件的随机读写

深入理解文件随机读写

文件随机读写是C语言中灵活访问文件内容的重要方式。东巴文(db-w.cn) 将带你深入理解文件随机读写的原理与实践。

💡 东巴文观点:随机读写让你能够灵活地访问文件的任意位置,是处理复杂数据文件的关键技能。

什么是随机读写

随机读写的定义

#include <stdio.h>

void randomIODefinition() {
    printf("=== 东巴文随机读写的定义 ===\n\n");
    
    printf("随机读写(Random I/O):\n");
    printf("  可以在文件的任意位置进行读写\n");
    printf("  通过文件位置指针控制访问位置\n");
    printf("  适合处理结构化数据文件\n\n");
    
    printf("随机读写特点:\n");
    printf("  1. 灵活性高\n");
    printf("  2. 效率较高\n");
    printf("  3. 适合随机访问\n");
    printf("  4. 需要手动管理位置\n");
    
    printf("\n随机读写函数:\n");
    printf("  fseek - 设置文件位置\n");
    printf("  ftell - 获取文件位置\n");
    printf("  rewind - 重置文件位置\n");
    printf("  fgetpos - 获取文件位置\n");
    printf("  fsetpos - 设置文件位置\n");
}

int main() {
    randomIODefinition();
    return 0;
}

文件位置指针

#include <stdio.h>

void filePositionPointer() {
    printf("=== 东巴文文件位置指针 ===\n\n");
    
    printf("文件位置指针:\n");
    printf("  指向文件当前读写位置的指针\n");
    printf("  每次读写后自动移动\n");
    printf("  可以手动调整位置\n\n");
    
    printf("位置指针特性:\n");
    printf("  1. 从文件开头计算(0开始)\n");
    printf("  2. 每次读写后自动更新\n");
    printf("  3. 可以获取当前位置\n");
    printf("  4. 可以设置任意位置\n");
    
    printf("\n位置指针操作:\n");
    printf("  读取:位置指针后移\n");
    printf("  写入:位置指针后移\n");
    printf("  定位:位置指针跳转\n");
    printf("  查询:获取当前位置\n");
}

int main() {
    filePositionPointer();
    return 0;
}

文件定位函数

fseek函数

#include <stdio.h>
#include <stdlib.h>

void fseekFunction() {
    printf("=== 东巴文fseek函数 ===\n\n");
    
    printf("函数原型:\n");
    printf("  int fseek(FILE* stream, long offset, int whence);\n\n");
    
    printf("功能:\n");
    printf("  设置文件位置指针\n\n");
    
    printf("参数:\n");
    printf("  stream - 文件指针\n");
    printf("  offset - 偏移量\n");
    printf("  whence - 起始位置\n\n");
    
    printf("whence参数:\n");
    printf("  SEEK_SET (0) - 文件开头\n");
    printf("  SEEK_CUR (1) - 当前位置\n");
    printf("  SEEK_END (2) - 文件末尾\n\n");
    
    printf("返回值:\n");
    printf("  成功:0\n");
    printf("  失败:-1\n");
    
    // 示例:创建测试文件
    FILE *fp = fopen("test.txt", "w+");
    if (fp == NULL) {
        perror("打开文件失败");
        return;
    }
    
    // 写入数据
    fprintf(fp, "0123456789ABCDEFGHIJ");
    
    // 定位到第5个字节
    fseek(fp, 5, SEEK_SET);
    char ch = fgetc(fp);
    printf("\n第5个字节:%c\n", ch);
    
    // 定位到倒数第5个字节
    fseek(fp, -5, SEEK_END);
    ch = fgetc(fp);
    printf("倒数第5个字节:%c\n", ch);
    
    // 从当前位置后移3个字节
    fseek(fp, 3, SEEK_CUR);
    ch = fgetc(fp);
    printf("当前位置后移3个字节:%c\n", ch);
    
    fclose(fp);
}

int main() {
    fseekFunction();
    return 0;
}

ftell函数

#include <stdio.h>
#include <stdlib.h>

void ftellFunction() {
    printf("=== 东巴文ftell函数 ===\n\n");
    
    printf("函数原型:\n");
    printf("  long ftell(FILE* stream);\n\n");
    
    printf("功能:\n");
    printf("  获取当前文件位置\n\n");
    
    printf("参数:\n");
    printf("  stream - 文件指针\n\n");
    
    printf("返回值:\n");
    printf("  成功:当前位置(相对于文件开头)\n");
    printf("  失败:-1L\n");
    
    // 示例
    FILE *fp = fopen("test.txt", "w+");
    if (fp == NULL) {
        perror("打开文件失败");
        return;
    }
    
    fprintf(fp, "东巴文db-w.cn");
    
    // 获取当前位置
    long pos = ftell(fp);
    printf("\n当前位置:%ld\n", pos);
    
    // 定位到开头
    fseek(fp, 0, SEEK_SET);
    pos = ftell(fp);
    printf("定位到开头后:%ld\n", pos);
    
    // 读取一个字符
    fgetc(fp);
    pos = ftell(fp);
    printf("读取一个字符后:%ld\n", pos);
    
    fclose(fp);
    
    printf("\n应用场景:\n");
    printf("  1. 获取文件大小\n");
    printf("  2. 记录读写位置\n");
    printf("  3. 计算相对位置\n");
    printf("  4. 实现断点续传\n");
}

int main() {
    ftellFunction();
    return 0;
}

rewind函数

#include <stdio.h>
#include <stdlib.h>

void rewindFunction() {
    printf("=== 东巴文rewind函数 ===\n\n");
    
    printf("函数原型:\n");
    printf("  void rewind(FILE* stream);\n\n");
    
    printf("功能:\n");
    printf("  将文件位置指针重置到文件开头\n");
    printf("  清除错误标志和EOF标志\n\n");
    
    printf("参数:\n");
    printf("  stream - 文件指针\n\n");
    
    printf("等价于:\n");
    printf("  fseek(fp, 0L, SEEK_SET);\n");
    printf("  clearerr(fp);\n");
    
    // 示例
    FILE *fp = fopen("test.txt", "w+");
    if (fp == NULL) {
        perror("打开文件失败");
        return;
    }
    
    fprintf(fp, "东巴文\n第二行\n第三行\n");
    
    // 读取一遍
    printf("第一次读取:\n");
    rewind(fp);
    char buffer[100];
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        printf("  %s", buffer);
    }
    
    // 重置后再读取
    printf("\n第二次读取:\n");
    rewind(fp);
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        printf("  %s", buffer);
    }
    
    fclose(fp);
    
    printf("\n应用场景:\n");
    printf("  1. 重新读取文件\n");
    printf("  2. 多次处理文件\n");
    printf("  3. 清除错误状态\n");
}

int main() {
    rewindFunction();
    return 0;
}

fgetpos和fsetpos函数

#include <stdio.h>
#include <stdlib.h>

void fgetposFsetpos() {
    printf("=== 东巴文fgetpos和fsetpos函数 ===\n\n");
    
    printf("fgetpos函数原型:\n");
    printf("  int fgetpos(FILE* stream, fpos_t* pos);\n\n");
    
    printf("fsetpos函数原型:\n");
    printf("  int fsetpos(FILE* stream, const fpos_t* pos);\n\n");
    
    printf("功能:\n");
    printf("  fgetpos - 获取文件位置\n");
    printf("  fsetpos - 设置文件位置\n\n");
    
    printf("优势:\n");
    printf("  1. 支持大文件(>2GB)\n");
    printf("  2. 可移植性好\n");
    printf("  3. 类型安全\n");
    
    // 示例
    FILE *fp = fopen("test.txt", "w+");
    if (fp == NULL) {
        perror("打开文件失败");
        return;
    }
    
    fprintf(fp, "东巴文db-w.cn");
    
    // 保存位置
    fpos_t pos;
    fgetpos(fp, &pos);
    printf("\n保存当前位置\n");
    
    // 移动位置
    fseek(fp, 0, SEEK_SET);
    printf("移动到开头\n");
    
    // 恢复位置
    fsetpos(fp, &pos);
    printf("恢复位置\n");
    printf("当前位置:%ld\n", ftell(fp));
    
    fclose(fp);
    
    printf("\n使用建议:\n");
    printf("  1. 大文件使用fgetpos/fsetpos\n");
    printf("  2. 小文件使用ftell/fseek\n");
    printf("  3. 需要保存位置时使用\n");
}

int main() {
    fgetposFsetpos();
    return 0;
}

随机读写实例

实例1:获取文件大小

#include <stdio.h>
#include <stdlib.h>

long getFileSize(const char *filename) {
    FILE *fp = fopen(filename, "rb");
    if (fp == NULL) {
        return -1;
    }
    
    fseek(fp, 0, SEEK_END);
    long size = ftell(fp);
    fclose(fp);
    
    return size;
}

void getFileSizeExample() {
    printf("=== 东巴文获取文件大小 ===\n\n");
    
    // 创建测试文件
    FILE *fp = fopen("test.txt", "w");
    if (fp != NULL) {
        fprintf(fp, "东巴文db-w.cn\n");
        fprintf(fp, "文件大小测试\n");
        fclose(fp);
    }
    
    // 获取文件大小
    long size = getFileSize("test.txt");
    if (size >= 0) {
        printf("文件大小:%ld 字节\n", size);
    } else {
        printf("获取文件大小失败\n");
    }
    
    printf("\n获取文件大小步骤:\n");
    printf("  1. 打开文件\n");
    printf("  2. 定位到文件末尾\n");
    printf("  3. 获取当前位置\n");
    printf("  4. 关闭文件\n");
}

int main() {
    getFileSizeExample();
    return 0;
}

实例2:读取文件指定位置

#include <stdio.h>
#include <stdlib.h>

void readAtPosition() {
    printf("=== 东巴文读取文件指定位置 ===\n\n");
    
    // 创建测试文件
    FILE *fp = fopen("data.txt", "w+");
    if (fp == NULL) {
        perror("打开文件失败");
        return;
    }
    
    // 写入数据
    for (int i = 0; i < 10; i++) {
        fprintf(fp, "数据%02d\n", i);
    }
    
    // 读取第5行
    fseek(fp, 0, SEEK_SET);
    char buffer[100];
    for (int i = 0; i < 5; i++) {
        if (fgets(buffer, sizeof(buffer), fp) == NULL) {
            break;
        }
    }
    
    printf("第5行:%s", buffer);
    
    // 读取最后一行
    fseek(fp, -20, SEEK_END);  // 假设每行约20字节
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        // 读取到最后一行
    }
    printf("最后一行:%s", buffer);
    
    fclose(fp);
    
    printf("\n随机读取应用:\n");
    printf("  1. 读取特定记录\n");
    printf("  2. 跳过部分内容\n");
    printf("  3. 实现分页显示\n");
    printf("  4. 数据库查询\n");
}

int main() {
    readAtPosition();
    return 0;
}

实例3:修改文件内容

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void modifyFileContent() {
    printf("=== 东巴文修改文件内容 ===\n\n");
    
    // 创建测试文件
    FILE *fp = fopen("modify.txt", "w+");
    if (fp == NULL) {
        perror("打开文件失败");
        return;
    }
    
    // 写入初始内容
    fprintf(fp, "原始内容第一行\n");
    fprintf(fp, "原始内容第二行\n");
    fprintf(fp, "原始内容第三行\n");
    
    // 定位到第二行开头
    fseek(fp, 0, SEEK_SET);
    char buffer[100];
    fgets(buffer, sizeof(buffer), fp);  // 跳过第一行
    
    // 记录第二行位置
    long pos = ftell(fp);
    
    // 读取第二行
    fgets(buffer, sizeof(buffer), fp);
    printf("原始第二行:%s", buffer);
    
    // 修改第二行
    fseek(fp, pos, SEEK_SET);
    fprintf(fp, "修改后的第二行\n");
    
    // 读取验证
    fseek(fp, 0, SEEK_SET);
    printf("\n修改后的文件内容:\n");
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        printf("  %s", buffer);
    }
    
    fclose(fp);
    
    printf("\n修改文件注意事项:\n");
    printf("  1. 新内容长度要匹配\n");
    printf("  2. 注意文件位置\n");
    printf("  3. 及时刷新缓冲区\n");
    printf("  4. 备份重要文件\n");
}

int main() {
    modifyFileContent();
    return 0;
}

实例4:结构化数据随机访问

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    int id;
    char name[50];
    int age;
    float score;
} Student;

void structuredDataAccess() {
    printf("=== 东巴文结构化数据随机访问 ===\n\n");
    
    // 创建学生数据文件
    FILE *fp = fopen("students.dat", "wb+");
    if (fp == NULL) {
        perror("打开文件失败");
        return;
    }
    
    // 写入学生数据
    Student students[5] = {
        {1, "东巴文", 20, 95.5f},
        {2, "张三", 21, 88.0f},
        {3, "李四", 22, 92.5f},
        {4, "王五", 20, 85.0f},
        {5, "赵六", 21, 90.0f}
    };
    
    fwrite(students, sizeof(Student), 5, fp);
    
    // 随机访问第3个学生
    Student student;
    fseek(fp, 2 * sizeof(Student), SEEK_SET);
    fread(&student, sizeof(Student), 1, fp);
    
    printf("第3个学生:\n");
    printf("  ID:%d\n", student.id);
    printf("  姓名:%s\n", student.name);
    printf("  年龄:%d\n", student.age);
    printf("  成绩:%.1f\n", student.score);
    
    // 修改第3个学生
    student.score = 99.0f;
    fseek(fp, 2 * sizeof(Student), SEEK_SET);
    fwrite(&student, sizeof(Student), 1, fp);
    
    printf("\n修改后的第3个学生成绩:%.1f\n", student.score);
    
    // 遍历所有学生
    printf("\n所有学生:\n");
    fseek(fp, 0, SEEK_SET);
    while (fread(&student, sizeof(Student), 1, fp) == 1) {
        printf("  %d. %s, %d岁, 成绩%.1f\n", 
               student.id, student.name, student.age, student.score);
    }
    
    fclose(fp);
    
    printf("\n结构化数据访问优势:\n");
    printf("  1. 快速定位记录\n");
    printf("  2. 高效修改数据\n");
    printf("  3. 节省存储空间\n");
    printf("  4. 适合数据库应用\n");
}

int main() {
    structuredDataAccess();
    return 0;
}

东巴文最佳实践

1. 正确使用定位函数

#include <stdio.h>
#include <stdlib.h>

void usePositioningFunctions() {
    printf("=== 东巴文正确使用定位函数 ===\n\n");
    
    FILE *fp = fopen("test.txt", "w+");
    if (fp == NULL) {
        perror("打开文件失败");
        return;
    }
    
    fprintf(fp, "东巴文db-w.cn");
    
    // ✅ 正确:检查fseek返回值
    if (fseek(fp, 0, SEEK_SET) != 0) {
        perror("定位失败");
        fclose(fp);
        return;
    }
    
    // ✅ 正确:检查ftell返回值
    long pos = ftell(fp);
    if (pos == -1L) {
        perror("获取位置失败");
        fclose(fp);
        return;
    }
    
    printf("当前位置:%ld\n", pos);
    
    fclose(fp);
    
    printf("\n使用建议:\n");
    printf("  1. 检查定位函数返回值\n");
    printf("  2. 注意文件打开模式\n");
    printf("  3. 避免越界访问\n");
    printf("  4. 处理大文件使用fgetpos/fsetpos\n");
}

int main() {
    usePositioningFunctions();
    return 0;
}

2. 避免常见错误

#include <stdio.h>
#include <stdlib.h>

void avoidCommonErrors() {
    printf("=== 东巴文避免常见错误 ===\n\n");
    
    printf("常见错误:\n\n");
    
    printf("1. 忘记检查返回值:\n");
    printf("   ❌ fseek(fp, 100, SEEK_SET);\n");
    printf("   ✅ if (fseek(fp, 100, SEEK_SET) != 0) { /* 处理错误 */ }\n\n");
    
    printf("2. 越界访问:\n");
    printf("   ❌ fseek(fp, -100, SEEK_SET);  // 负偏移\n");
    printf("   ✅ 先检查文件大小\n\n");
    
    printf("3. 模式不匹配:\n");
    printf("   ❌ 用\"w\"模式打开后尝试读取\n");
    printf("   ✅ 使用\"w+\"或\"r+\"模式\n\n");
    
    printf("4. 未刷新缓冲区:\n");
    printf("   ❌ 写入后立即定位读取\n");
    printf("   ✅ 使用fflush或fclose刷新\n");
    
    printf("\n避免建议:\n");
    printf("  1. 总是检查返回值\n");
    printf("  2. 验证偏移量\n");
    printf("  3. 选择正确的模式\n");
    printf("  4. 及时刷新缓冲区\n");
}

int main() {
    avoidCommonErrors();
    return 0;
}

3. 性能优化

#include <stdio.h>
#include <stdlib.h>

void performanceOptimization() {
    printf("=== 东巴文性能优化 ===\n\n");
    
    printf("性能优化技巧:\n\n");
    
    printf("1. 减少定位操作:\n");
    printf("   - 批量读写数据\n");
    printf("   - 避免频繁定位\n");
    printf("   - 使用顺序读写\n\n");
    
    printf("2. 使用合适的缓冲区:\n");
    printf("   - 设置大缓冲区\n");
    printf("   - 使用setvbuf函数\n");
    printf("   - 减少磁盘I/O\n\n");
    
    printf("3. 合理的文件结构:\n");
    printf("   - 固定大小记录\n");
    printf("   - 索引文件\n");
    printf("   - 分块存储\n\n");
    
    printf("4. 缓存常用数据:\n");
    printf("   - 内存缓存\n");
    printf("   - 预读取数据\n");
    printf("   - 延迟写入\n");
    
    printf("\n优化建议:\n");
    printf("  1. 分析访问模式\n");
    printf("  2. 选择合适的策略\n");
    printf("  3. 测试性能\n");
    printf("  4. 持续优化\n");
}

int main() {
    performanceOptimization();
    return 0;
}

4. 错误处理

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

void errorHandling() {
    printf("=== 东巴文错误处理 ===\n\n");
    
    FILE *fp = fopen("test.txt", "w+");
    if (fp == NULL) {
        perror("打开文件失败");
        return;
    }
    
    // 写入数据
    if (fprintf(fp, "东巴文db-w.cn") < 0) {
        perror("写入失败");
        fclose(fp);
        return;
    }
    
    // 定位操作
    if (fseek(fp, 0, SEEK_SET) != 0) {
        perror("定位失败");
        fclose(fp);
        return;
    }
    
    // 获取位置
    long pos = ftell(fp);
    if (pos == -1L) {
        perror("获取位置失败");
        fclose(fp);
        return;
    }
    
    printf("当前位置:%ld\n", pos);
    
    // 检查错误标志
    if (ferror(fp)) {
        perror("文件错误");
        clearerr(fp);
    }
    
    fclose(fp);
    
    printf("\n错误处理建议:\n");
    printf("  1. 检查所有操作返回值\n");
    printf("  2. 使用perror输出错误\n");
    printf("  3. 使用ferror检查错误\n");
    printf("  4. 使用clearerr清除错误\n");
}

int main() {
    errorHandling();
    return 0;
}

东巴文验证清单

完成本章学习后,请确认:

  • 理解随机读写的概念
  • 掌握fseek函数
  • 掌握ftell函数
  • 掌握rewind函数
  • 掌握fgetpos/fsetpos函数
  • 掌握随机读写实例

下一步学习

掌握文件随机读写后,你可以继续学习:

如果遇到问题,欢迎访问 东巴文(db-w.cn) 获取帮助!


东巴文(db-w.cn) - 让编程学习更简单

🎯 东巴文随机读写提示:随机读写是处理复杂数据文件的关键技能。在 db-w.cn,我们会通过大量实例帮你掌握文件操作!