Skip to content

文件操作

文件操作是C语言程序设计中重要的内容,通过文件可以持久化存储数据。本章将详细介绍C语言的文件读写操作。

文件基础

文件的打开和关闭

c
/*
 * 文件的打开和关闭
 */

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

int main(void)
{
    FILE *fp;
    
    /* fopen - 打开文件 */
    /* 模式:
     * "r"  - 只读(文件必须存在)
     * "w"  - 只写(文件不存在则创建,存在则清空)
     * "a"  - 追加(文件不存在则创建)
     * "r+" - 读写(文件必须存在)
     * "w+" - 读写(文件不存在则创建,存在则清空)
     * "a+" - 读写追加(文件不存在则创建)
     * "rb", "wb", "ab" - 二进制模式
     */
    
    /* 打开文件用于写入 */
    fp = fopen("test.txt", "w");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }
    
    printf("文件打开成功\n");
    
    /* 写入一些内容 */
    fprintf(fp, "Hello, File!\n");
    fprintf(fp, "这是第二行\n");
    
    /* fclose - 关闭文件 */
    fclose(fp);
    printf("文件已关闭\n");
    
    /* 重新打开用于读取 */
    fp = fopen("test.txt", "r");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }
    
    /* 读取并显示内容 */
    char buffer[100];
    printf("\n文件内容:\n");
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        printf("%s", buffer);
    }
    
    fclose(fp);
    
    return 0;
}

文件读写函数

c
/*
 * 文件读写函数
 */

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

int main(void)
{
    FILE *fp;
    
    /* === 字符读写 === */
    
    /* 写入字符 */
    fp = fopen("char_test.txt", "w");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }
    
    fputc('H', fp);     /* 写入单个字符 */
    fputc('i', fp);
    fputc('\n', fp);
    
    fputs("Hello\n", fp);  /* 写入字符串 */
    fputs("World\n", fp);
    
    fclose(fp);
    
    /* 读取字符 */
    fp = fopen("char_test.txt", "r");
    if (fp != NULL) {
        printf("字符读取:\n");
        int ch;
        while ((ch = fgetc(fp)) != EOF) {  /* EOF表示文件结束 */
            putchar(ch);
        }
        fclose(fp);
    }
    
    /* === 格式化读写 === */
    
    fp = fopen("format_test.txt", "w");
    if (fp != NULL) {
        /* fprintf - 格式化写入 */
        fprintf(fp, "姓名: %s\n", "张三");
        fprintf(fp, "年龄: %d\n", 25);
        fprintf(fp, "成绩: %.1f\n", 85.5);
        fclose(fp);
    }
    
    /* fscanf - 格式化读取 */
    fp = fopen("format_test.txt", "r");
    if (fp != NULL) {
        char name[50];
        int age;
        float score;
        
        /* 跳过"姓名: " */
        fscanf(fp, "姓名: %s\n", name);
        fscanf(fp, "年龄: %d\n", &age);
        fscanf(fp, "成绩: %f\n", &score);
        
        printf("\n格式化读取:\n");
        printf("姓名: %s\n", name);
        printf("年龄: %d\n", age);
        printf("成绩: %.1f\n", score);
        
        fclose(fp);
    }
    
    return 0;
}

二进制文件读写

c
/*
 * 二进制文件读写
 */

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

/* 结构体定义 */
struct Student {
    char name[50];
    int age;
    float score;
};

int main(void)
{
    struct Student students[] = {
        {"张三", 20, 85.5},
        {"李四", 21, 90.0},
        {"王五", 22, 78.5}
    };
    int count = 3;
    
    /* 写入二进制文件 */
    FILE *fp = fopen("students.dat", "wb");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }
    
    /* fwrite - 写入二进制数据 */
    /* fwrite(数据地址, 每个元素大小, 元素个数, 文件指针) */
    fwrite(&count, sizeof(int), 1, fp);  /* 先写入记录数 */
    fwrite(students, sizeof(struct Student), count, fp);
    
    fclose(fp);
    printf("已写入%d条学生记录\n", count);
    
    /* 读取二进制文件 */
    fp = fopen("students.dat", "rb");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }
    
    /* fread - 读取二进制数据 */
    int read_count;
    fread(&read_count, sizeof(int), 1, fp);
    
    printf("\n读取到%d条记录:\n", read_count);
    
    struct Student *read_students = (struct Student*)malloc(
        read_count * sizeof(struct Student));
    
    if (read_students != NULL) {
        fread(read_students, sizeof(struct Student), read_count, fp);
        
        for (int i = 0; i < read_count; i++) {
            printf("学生%d: %s, %d岁, 成绩%.1f\n",
                   i + 1, read_students[i].name, 
                   read_students[i].age, read_students[i].score);
        }
        
        free(read_students);
    }
    
    fclose(fp);
    
    return 0;
}

文件定位

c
/*
 * 文件定位函数
 */

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

int main(void)
{
    FILE *fp;
    
    /* 创建测试文件 */
    fp = fopen("seek_test.txt", "w");
    if (fp != NULL) {
        fprintf(fp, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        fclose(fp);
    }
    
    /* 打开文件进行随机访问 */
    fp = fopen("seek_test.txt", "r");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }
    
    /* ftell - 获取当前位置 */
    long pos = ftell(fp);
    printf("当前位置: %ld\n", pos);
    
    /* fseek - 移动文件指针 */
    /* fseek(文件指针, 偏移量, 起始位置)
     * SEEK_SET - 文件开头
     * SEEK_CUR - 当前位置
     * SEEK_END - 文件末尾
     */
    
    /* 移动到第10个字符 */
    fseek(fp, 10, SEEK_SET);
    char ch = fgetc(fp);
    printf("第10个字符: %c\n", ch);
    
    /* 从当前位置移动 */
    fseek(fp, 5, SEEK_CUR);
    ch = fgetc(fp);
    printf("当前位置+5的字符: %c\n", ch);
    
    /* 移动到文件末尾 */
    fseek(fp, 0, SEEK_END);
    long file_size = ftell(fp);
    printf("文件大小: %ld 字节\n", file_size);
    
    /* 从末尾向前移动 */
    fseek(fp, -5, SEEK_END);
    printf("最后5个字符: ");
    for (int i = 0; i < 5; i++) {
        putchar(fgetc(fp));
    }
    printf("\n");
    
    /* rewind - 回到文件开头 */
    rewind(fp);
    printf("\n回到开头后第一个字符: %c\n", fgetc(fp));
    
    fclose(fp);
    
    return 0;
}

错误处理

c
/*
 * 文件错误处理
 */

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

int main(void)
{
    FILE *fp;
    
    /* 尝试打开不存在的文件 */
    fp = fopen("nonexistent.txt", "r");
    if (fp == NULL) {
        /* perror - 打印错误信息 */
        perror("打开文件失败");
        
        /* errno - 错误码 */
        printf("错误码: %d\n", errno);
        
        /* strerror - 错误描述 */
        printf("错误描述: %s\n", strerror(errno));
    }
    
    /* ferror - 检查文件错误 */
    fp = fopen("error_test.txt", "w");
    if (fp != NULL) {
        /* 尝试从只写文件读取 */
        /* int ch = fgetc(fp); */  /* 会返回EOF */
        
        if (ferror(fp)) {
            printf("文件发生错误\n");
            clearerr(fp);  /* 清除错误标志 */
        }
        
        fclose(fp);
    }
    
    /* feof - 检查文件结束 */
    fp = fopen("error_test.txt", "r");
    if (fp != NULL) {
        printf("\n读取到文件末尾:\n");
        while (!feof(fp)) {
            int ch = fgetc(fp);
            if (ch != EOF) {
                putchar(ch);
            }
        }
        
        if (feof(fp)) {
            printf("\n已到达文件末尾\n");
        }
        
        fclose(fp);
    }
    
    /* 安全的文件操作 */
    printf("\n安全的文件操作示例:\n");
    
    fp = fopen("safe_test.txt", "w");
    if (fp == NULL) {
        perror("无法创建文件");
        return 1;
    }
    
    if (fprintf(fp, "测试内容\n") < 0) {
        perror("写入失败");
        fclose(fp);
        return 1;
    }
    
    if (fclose(fp) != 0) {
        perror("关闭文件失败");
        return 1;
    }
    
    printf("文件操作成功\n");
    
    return 0;
}

文件操作实例

复制文件

c
/*
 * 文件复制
 */

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

int copy_file(const char *src, const char *dest)
{
    FILE *fp_src = fopen(src, "rb");
    if (fp_src == NULL) {
        perror("打开源文件失败");
        return -1;
    }
    
    FILE *fp_dest = fopen(dest, "wb");
    if (fp_dest == NULL) {
        perror("创建目标文件失败");
        fclose(fp_src);
        return -1;
    }
    
    /* 缓冲区复制 */
    char buffer[4096];
    size_t bytes_read;
    
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), fp_src)) > 0) {
        if (fwrite(buffer, 1, bytes_read, fp_dest) != bytes_read) {
            perror("写入失败");
            fclose(fp_src);
            fclose(fp_dest);
            return -1;
        }
    }
    
    fclose(fp_src);
    fclose(fp_dest);
    
    return 0;
}

int main(void)
{
    /* 创建源文件 */
    FILE *fp = fopen("source.txt", "w");
    if (fp != NULL) {
        fprintf(fp, "这是源文件的内容\n");
        fprintf(fp, "第二行\n");
        fprintf(fp, "第三行\n");
        fclose(fp);
    }
    
    /* 复制文件 */
    if (copy_file("source.txt", "destination.txt") == 0) {
        printf("文件复制成功\n");
    } else {
        printf("文件复制失败\n");
    }
    
    return 0;
}

统计文件信息

c
/*
 * 统计文件信息
 */

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

int main(void)
{
    char filename[100];
    printf("请输入文件名: ");
    scanf("%s", filename);
    
    FILE *fp = fopen(filename, "r");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }
    
    int chars = 0, words = 0, lines = 0;
    int in_word = 0;  /* 是否在单词中 */
    int ch;
    
    while ((ch = fgetc(fp)) != EOF) {
        chars++;
        
        /* 统计行数 */
        if (ch == '\n') {
            lines++;
        }
        
        /* 统计单词数 */
        if (isspace(ch)) {
            in_word = 0;
        } else if (!in_word) {
            in_word = 1;
            words++;
        }
    }
    
    /* 如果文件不为空且不以换行结尾,行数+1 */
    if (chars > 0 && ch != '\n') {
        lines++;
    }
    
    printf("\n文件统计:\n");
    printf("字符数: %d\n", chars);
    printf("单词数: %d\n", words);
    printf("行数: %d\n", lines);
    
    fclose(fp);
    
    return 0;
}

小结

本章学习了:

  • 文件打开关闭:fopen、fclose、文件模式
  • 文件读写:fgetc、fputc、fgets、fputs、fprintf、fscanf
  • 二进制文件:fread、fwrite
  • 文件定位:fseek、ftell、rewind
  • 错误处理:ferror、feof、clearerr、perror
  • 文件操作实例:文件复制、文件统计

下一章,我们将学习内存管理,了解C语言的动态内存分配。