Skip to content

文件读写

在C语言中,文件操作是程序与外部数据交互的重要方式。C语言提供了一组丰富的文件I/O函数,用于打开、读取、写入和关闭文件。

文件指针

在C语言中,文件通过文件指针来操作。文件指针是一个指向FILE结构体的指针,该结构体包含了文件的所有信息,如文件名、文件位置指示器、文件状态等。

c
FILE *fp;

打开文件

使用fopen()函数打开文件:

c
FILE *fopen(const char *filename, const char *mode);

文件打开模式

模式描述文件不存在时
"r"只读,文件必须存在返回NULL
"w"只写,覆盖原有内容创建新文件
"a"追加,在文件末尾写入创建新文件
"r+"读写,文件必须存在返回NULL
"w+"读写,覆盖原有内容创建新文件
"a+"读写,在文件末尾写入创建新文件
"rb"二进制只读返回NULL
"wb"二进制只写创建新文件
"ab"二进制追加创建新文件
c
#include <stdio.h>

int main() {
    FILE *fp;

    // 以只读模式打开文件
    fp = fopen("data.txt", "r");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }

    // 文件操作...

    fclose(fp);
    return 0;
}

关闭文件

使用fclose()函数关闭文件:

c
int fclose(FILE *stream);

关闭文件时,所有缓冲的数据都会被写入文件。成功关闭返回0,失败返回EOF。

c
if (fclose(fp) != 0) {
    printf("关闭文件时出错\n");
}

字符级文件操作

写入字符

c
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
c
#include <stdio.h>

int main() {
    FILE *fp = fopen("output.txt", "w");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }

    // 写入单个字符
    fputc('H', fp);
    fputc('e', fp);
    fputc('l', fp);
    fputc('l', fp);
    fputc('o', fp);

    fclose(fp);
    return 0;
}

读取字符

c
int fgetc(FILE *stream);
int getc(FILE *stream);
c
#include <stdio.h>

int main() {
    FILE *fp = fopen("input.txt", "r");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }

    int ch;
    while ((ch = fgetc(fp)) != EOF) {
        putchar(ch);
    }

    fclose(fp);
    return 0;
}

字符串级文件操作

写入字符串

c
int fputs(const char *s, FILE *stream);
c
#include <stdio.h>

int main() {
    FILE *fp = fopen("output.txt", "w");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }

    fputs("Hello, World!\n", fp);
    fputs("This is a test.\n", fp);

    fclose(fp);
    return 0;
}

读取字符串

c
char *fgets(char *s, int size, FILE *stream);

fgets()读取一行字符串,包括换行符,最多读取size-1个字符。

c
#include <stdio.h>

int main() {
    FILE *fp = fopen("input.txt", "r");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }

    char buffer[256];
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        printf("%s", buffer);
    }

    fclose(fp);
    return 0;
}

格式化文件操作

格式化写入

c
int fprintf(FILE *stream, const char *format, ...);
c
#include <stdio.h>

int main() {
    FILE *fp = fopen("data.txt", "w");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }

    int age = 25;
    float score = 95.5;
    char name[] = "张三";

    fprintf(fp, "姓名: %s\n", name);
    fprintf(fp, "年龄: %d\n", age);
    fprintf(fp, "分数: %.2f\n", score);

    fclose(fp);
    return 0;
}

格式化读取

c
int fscanf(FILE *stream, const char *format, ...);
c
#include <stdio.h>

int main() {
    FILE *fp = fopen("data.txt", "r");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }

    char name[50];
    int age;
    float score;

    fscanf(fp, "姓名: %s\n", name);
    fscanf(fp, "年龄: %d\n", &age);
    fscanf(fp, "分数: %f\n", &score);

    printf("姓名: %s\n", name);
    printf("年龄: %d\n", age);
    printf("分数: %.2f\n", score);

    fclose(fp);
    return 0;
}

二进制文件操作

写入二进制数据

c
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
c
#include <stdio.h>

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

int main() {
    FILE *fp = fopen("students.dat", "wb");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }

    Student students[] = {
        {"张三", 20, 85.5},
        {"李四", 21, 92.0},
        {"王五", 19, 78.5}
    };

    int count = sizeof(students) / sizeof(students[0]);
    fwrite(students, sizeof(Student), count, fp);

    fclose(fp);
    return 0;
}

读取二进制数据

c
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
c
#include <stdio.h>

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

int main() {
    FILE *fp = fopen("students.dat", "rb");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }

    Student students[3];
    fread(students, sizeof(Student), 3, fp);

    for (int i = 0; i < 3; i++) {
        printf("姓名: %s, 年龄: %d, 分数: %.2f\n",
               students[i].name, students[i].age, students[i].score);
    }

    fclose(fp);
    return 0;
}

文件定位

获取当前位置

c
long ftell(FILE *stream);

返回当前文件位置指示器的值。

c
#include <stdio.h>

int main() {
    FILE *fp = fopen("data.txt", "r");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }

    long pos = ftell(fp);
    printf("当前位置: %ld\n", pos);

    fclose(fp);
    return 0;
}

设置文件位置

c
int fseek(FILE *stream, long offset, int whence);

whence参数:

  • SEEK_SET: 从文件开头开始
  • SEEK_CUR: 从当前位置开始
  • SEEK_END: 从文件末尾开始
c
#include <stdio.h>

int main() {
    FILE *fp = fopen("data.txt", "r");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }

    // 移动到文件开头
    fseek(fp, 0, SEEK_SET);

    // 移动到文件末尾
    fseek(fp, 0, SEEK_END);

    // 从当前位置向前移动10个字节
    fseek(fp, -10, SEEK_CUR);

    fclose(fp);
    return 0;
}

重置文件位置

c
void rewind(FILE *stream);

将文件位置指示器重置到文件开头。

c
#include <stdio.h>

int main() {
    FILE *fp = fopen("data.txt", "r");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }

    // 读取文件
    char ch;
    while ((ch = fgetc(fp)) != EOF) {
        putchar(ch);
    }

    // 重置到文件开头
    rewind(fp);

    // 再次读取文件
    printf("\n--- 第二次读取 ---\n");
    while ((ch = fgetc(fp)) != EOF) {
        putchar(ch);
    }

    fclose(fp);
    return 0;
}

文件结束检测

c
int feof(FILE *stream);

检测是否到达文件末尾。

c
#include <stdio.h>

int main() {
    FILE *fp = fopen("data.txt", "r");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }

    char ch;
    while (!feof(fp)) {
        ch = fgetc(fp);
        if (ch != EOF) {
            putchar(ch);
        }
    }

    fclose(fp);
    return 0;
}

文件错误检测

c
int ferror(FILE *stream);

检测文件操作是否出错。

c
#include <stdio.h>

int main() {
    FILE *fp = fopen("data.txt", "r");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }

    char ch;
    while ((ch = fgetc(fp)) != EOF) {
        putchar(ch);
    }

    if (ferror(fp)) {
        printf("\n文件读取出错\n");
    }

    fclose(fp);
    return 0;
}

标准文件流

C语言提供了三个标准文件流:

文件流描述默认设备
stdin标准输入键盘
stdout标准输出屏幕
stderr标准错误屏幕
c
#include <stdio.h>

int main() {
    // 从标准输入读取
    printf("请输入一个整数: ");
    int num;
    scanf("%d", &num);

    // 输出到标准输出
    fprintf(stdout, "你输入的数字是: %d\n", num);

    // 输出到标准错误
    fprintf(stderr, "这是一个错误信息\n");

    return 0;
}

完整示例:学生成绩管理系统

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

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

void addStudent() {
    FILE *fp = fopen("students.dat", "ab");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return;
    }

    Student s;
    printf("学号: ");
    scanf("%d", &s.id);
    printf("姓名: ");
    scanf("%s", s.name);
    printf("分数: ");
    scanf("%f", &s.score);

    fwrite(&s, sizeof(Student), 1, fp);
    fclose(fp);
    printf("学生信息已添加\n");
}

void displayStudents() {
    FILE *fp = fopen("students.dat", "rb");
    if (fp == NULL) {
        printf("没有学生记录\n");
        return;
    }

    Student s;
    printf("\n学号\t姓名\t分数\n");
    printf("------------------------\n");
    while (fread(&s, sizeof(Student), 1, fp) == 1) {
        printf("%d\t%s\t%.2f\n", s.id, s.name, s.score);
    }

    fclose(fp);
}

void searchStudent() {
    int id;
    printf("请输入要查找的学号: ");
    scanf("%d", &id);

    FILE *fp = fopen("students.dat", "rb");
    if (fp == NULL) {
        printf("没有学生记录\n");
        return;
    }

    Student s;
    int found = 0;
    while (fread(&s, sizeof(Student), 1, fp) == 1) {
        if (s.id == id) {
            printf("\n学号: %d\n", s.id);
            printf("姓名: %s\n", s.name);
            printf("分数: %.2f\n", s.score);
            found = 1;
            break;
        }
    }

    if (!found) {
        printf("未找到学号为 %d 的学生\n", id);
    }

    fclose(fp);
}

int main() {
    int choice;

    while (1) {
        printf("\n学生成绩管理系统\n");
        printf("1. 添加学生\n");
        printf("2. 显示所有学生\n");
        printf("3. 查找学生\n");
        printf("4. 退出\n");
        printf("请选择: ");
        scanf("%d", &choice);

        switch (choice) {
            case 1:
                addStudent();
                break;
            case 2:
                displayStudents();
                break;
            case 3:
                searchStudent();
                break;
            case 4:
                exit(0);
            default:
                printf("无效选择\n");
        }
    }

    return 0;
}

文件操作注意事项

  1. 检查文件是否成功打开

    c
    FILE *fp = fopen("file.txt", "r");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }
  2. 及时关闭文件

    c
    fclose(fp);
  3. 检查文件操作是否成功

    c
    if (fwrite(&data, sizeof(data), 1, fp) != 1) {
        printf("写入失败\n");
    }
  4. 处理二进制文件和文本文件的区别

    • 文本文件:以字符形式存储,适合存储文本数据
    • 二进制文件:以二进制形式存储,适合存储结构化数据
  5. 注意文件定位操作

    • 在读取和写入之间切换时,需要使用fseek()fflush()
    • 在追加模式下,所有写入操作都在文件末尾进行
  6. 错误处理

    c
    if (ferror(fp)) {
        perror("文件操作出错");
        clearerr(fp);
    }

总结

C语言的文件I/O操作提供了强大而灵活的功能,包括:

  • 文本文件和二进制文件的读写
  • 字符、字符串、格式化和块级数据操作
  • 文件定位和随机访问
  • 错误检测和处理

掌握文件操作对于编写实用的C程序非常重要,它使程序能够持久化数据、处理大量数据,并与外部世界进行数据交换。