欢迎光临
我们一直在努力

13-文件操作:数据的持久化存储

# 文件操作:数据的持久化存储

## 将数据保存到磁盘

在上一篇文章中,我们学习了结构体和共用体的使用。现在,让我们来学习C语言中的文件操作。文件操作允许我们将数据持久化存储到磁盘上,实现数据的长期保存和读取。

## 一、文件操作的基本概念

### 1.1 什么是文件

文件是存储在外部存储设备上的数据集合。在C语言中,文件分为两种类型:

| 文件类型 | 描述 | 特点 |
|———-|——|——|
| **文本文件** | 以字符形式存储 | 可读,占用空间较大 |
| **二进制文件** | 以二进制形式存储 | 高效,占用空间较小 |

### 1.2 文件指针

```c
#include <stdio.h>

int main() {
    // 声明文件指针
    FILE *fp;
    
    // 打开文件
    fp = fopen("example.txt", "w");
    
    if (fp == NULL) {
        printf("文件打开失败\\n");
        return 1;
    }
    
    // 写入内容
    fprintf(fp, "Hello, File!\\n");
    
    // 关闭文件
    fclose(fp);
    
    return 0;
}
```

### 1.3 文件打开模式

| 模式 | 描述 |
|——|——|
| `"r"` | 只读方式打开文本文件 |
| `"w"` | 只写方式打开文本文件,覆盖原有内容 |
| `"a"` | 追加方式打开文本文件 |
| `"r+"` | 读写方式打开文本文件 |
| `"w+"` | 读写方式创建新文件 |
| `"a+"` | 读写方式打开文件,追加模式 |
| `"rb"` | 只读方式打开二进制文件 |
| `"wb"` | 只写方式打开二进制文件 |
| `"ab"` | 追加方式打开二进制文件 |

## 二、文件的打开与关闭

### 2.1 fopen函数

```c
#include <stdio.h>

int main() {
    FILE *fp;
    
    // 打开文件
    fp = fopen("test.txt", "w");
    
    // 检查是否成功打开
    if (fp == NULL) {
        printf("文件打开失败!\\n");
        perror("原因");
        return 1;
    }
    
    printf("文件打开成功!\\n");
    
    // 关闭文件
    fclose(fp);
    
    return 0;
}
```

### 2.2 fclose函数

```c
#include <stdio.h>

int main() {
    FILE *fp = fopen("data.txt", "w");
    
    if (fp == NULL) {
        perror("文件打开失败");
        return 1;
    }
    
    // 使用文件…
    
    // 关闭文件
    if (fclose(fp) != 0) {
        printf("文件关闭失败!\\n");
        return 1;
    }
    
    printf("文件关闭成功!\\n");
    
    return 0;
}
```

## 三、文件的读写操作

### 3.1 字符读写

```c
#include <stdio.h>

int main() {
    FILE *fp;
    char ch;
    
    // 写入文件
    fp = fopen("chars.txt", "w");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }
    
    for (ch = 'A'; ch <= 'Z'; ch++) {
        fputc(ch, fp);
    }
    fclose(fp);
    
    // 读取文件
    fp = fopen("chars.txt", "r");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }
    
    while ((ch = fgetc(fp)) != EOF) {
        printf("%c ", ch);
    }
    printf("\\n");
    fclose(fp);
    
    return 0;
}
```

### 3.2 字符串读写

```c
#include <stdio.h>

int main() {
    FILE *fp;
    char buffer[100];
    
    // 写入字符串
    fp = fopen("strings.txt", "w");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }
    
    fputs("Hello, World!\\n", fp);
    fputs("这是第二行。\\n", fp);
    fclose(fp);
    
    // 读取字符串
    fp = fopen("strings.txt", "r");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }
    
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        printf("%s", buffer);
    }
    fclose(fp);
    
    return 0;
}
```

### 3.3 格式化读写

```c
#include <stdio.h>

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

int main() {
    struct Student s = {"张三", 25, 95.5f};
    FILE *fp;
    
    // 写入格式化数据
    fp = fopen("student.txt", "w");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }
    
    fprintf(fp, "%s %d %.1f\\n", s.name, s.age, s.score);
    fclose(fp);
    
    // 读取格式化数据
    struct Student s2;
    fp = fopen("student.txt", "r");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }
    
    fscanf(fp, "%s %d %f", s2.name, &s2.age, &s2.score);
    printf("姓名:%s,年龄:%d,成绩:%.1f\\n", s2.name, s2.age, s2.score);
    fclose(fp);
    
    return 0;
}
```

## 四、二进制文件操作

### 4.1 二进制写入

```c
#include <stdio.h>

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

int main() {
    struct Student students[3] = {
        {"张三", 25, 95.5f},
        {"李四", 23, 88.0f},
        {"王五", 24, 92.3f}
    };
    
    FILE *fp = fopen("students.dat", "wb");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }
    
    // 写入二进制数据
    fwrite(students, sizeof(struct Student), 3, fp);
    fclose(fp);
    
    printf("二进制文件写入成功!\\n");
    
    return 0;
}
```

### 4.2 二进制读取

```c
#include <stdio.h>

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

int main() {
    struct Student students[3];
    
    FILE *fp = fopen("students.dat", "rb");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }
    
    // 读取二进制数据
    fread(students, sizeof(struct Student), 3, fp);
    fclose(fp);
    
    // 输出数据
    for (int i = 0; i < 3; i++) {
        printf("学生%d:\\n", i + 1);
        printf("  姓名:%s\\n", students[i].name);
        printf("  年龄:%d\\n", students[i].age);
        printf("  成绩:%.1f\\n", students[i].score);
    }
    
    return 0;
}
```

## 五、文件定位

### 5.1 fseek函数

```c
#include <stdio.h>

int main() {
    FILE *fp = fopen("example.txt", "w+");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }
    
    // 写入内容
    fprintf(fp, "Hello, World!");
    
    // 将文件指针移动到文件开头
    fseek(fp, 0, SEEK_SET);
    
    // 读取内容
    char buffer[20];
    fgets(buffer, sizeof(buffer), fp);
    printf("读取内容:%s\\n", buffer);
    
    // 将文件指针移动到第7个字符
    fseek(fp, 6, SEEK_SET);
    fgets(buffer, sizeof(buffer), fp);
    printf("从第7个字符开始:%s\\n", buffer);
    
    // 将文件指针向前移动5个字符
    fseek(fp, -5, SEEK_CUR);
    fgets(buffer, sizeof(buffer), fp);
    printf("向前移动5个字符:%s\\n", buffer);
    
    fclose(fp);
    
    return 0;
}
```

### 5.2 ftell函数

```c
#include <stdio.h>

int main() {
    FILE *fp = fopen("example.txt", "r");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }
    
    // 获取当前位置
    long pos = ftell(fp);
    printf("初始位置:%ld\\n", pos);
    
    // 读取一些内容
    char buffer[10];
    fgets(buffer, sizeof(buffer), fp);
    
    // 获取当前位置
    pos = ftell(fp);
    printf("读取后的位置:%ld\\n", pos);
    
    fclose(fp);
    
    return 0;
}
```

### 5.3 rewind函数

```c
#include <stdio.h>

int main() {
    FILE *fp = fopen("example.txt", "r");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }
    
    // 读取第一行
    char buffer[50];
    fgets(buffer, sizeof(buffer), fp);
    printf("第一行:%s", buffer);
    
    // 回到文件开头
    rewind(fp);
    
    // 再次读取第一行
    fgets(buffer, sizeof(buffer), fp);
    printf("再次读取第一行:%s", buffer);
    
    fclose(fp);
    
    return 0;
}
```

## 六、文件操作的常见函数

### 6.1 常用函数汇总

| 函数 | 功能 | 示例 |
|——|——|——|
| `fopen` | 打开文件 | `fp = fopen("file.txt", "r");` |
| `fclose` | 关闭文件 | `fclose(fp);` |
| `fgetc` | 读取一个字符 | `ch = fgetc(fp);` |
| `fputc` | 写入一个字符 | `fputc('A', fp);` |
| `fgets` | 读取一行字符串 | `fgets(buf, size, fp);` |
| `fputs` | 写入一行字符串 | `fputs("hello", fp);` |
| `fscanf` | 格式化读取 | `fscanf(fp, "%d", &num);` |
| `fprintf` | 格式化写入 | `fprintf(fp, "%d", num);` |
| `fread` | 二进制读取 | `fread(buf, size, count, fp);` |
| `fwrite` | 二进制写入 | `fwrite(buf, size, count, fp);` |
| `fseek` | 文件定位 | `fseek(fp, offset, origin);` |
| `ftell` | 获取位置 | `pos = ftell(fp);` |
| `rewind` | 回到开头 | `rewind(fp);` |
| `feof` | 判断文件结束 | `while (!feof(fp))` |

## 七、文件操作的错误处理

### 7.1 检查文件是否存在

```c
#include <stdio.h>

int fileExists(const char *filename) {
    FILE *fp = fopen(filename, "r");
    if (fp != NULL) {
        fclose(fp);
        return 1;
    }
    return 0;
}

int main() {
    if (fileExists("example.txt")) {
        printf("文件存在\\n");
    } else {
        printf("文件不存在\\n");
    }
    
    return 0;
}
```

### 7.2 错误处理示例

```c
#include <stdio.h>

int main() {
    FILE *fp = fopen("nonexistent.txt", "r");
    
    if (fp == NULL) {
        perror("无法打开文件");
        printf("错误代码:%d\\n", ferror(fp));
        return 1;
    }
    
    // 检查是否到达文件末尾
    if (feof(fp)) {
        printf("已到达文件末尾\\n");
    }
    
    fclose(fp);
    
    return 0;
}
```

## 八、实战练习

### 练习1:文件复制

```c
#include <stdio.h>

int main() {
    FILE *source, *dest;
    char ch;
    
    source = fopen("source.txt", "r");
    if (source == NULL) {
        perror("无法打开源文件");
        return 1;
    }
    
    dest = fopen("dest.txt", "w");
    if (dest == NULL) {
        perror("无法打开目标文件");
        fclose(source);
        return 1;
    }
    
    // 逐字符复制
    while ((ch = fgetc(source)) != EOF) {
        fputc(ch, dest);
    }
    
    printf("文件复制成功!\\n");
    
    fclose(source);
    fclose(dest);
    
    return 0;
}
```

### 练习2:统计文件行数

```c
#include <stdio.h>

int countLines(const char *filename) {
    FILE *fp = fopen(filename, "r");
    if (fp == NULL) {
        perror("无法打开文件");
        return -1;
    }
    
    int count = 0;
    char ch;
    
    while ((ch = fgetc(fp)) != EOF) {
        if (ch == '\\n') {
            count++;
        }
    }
    
    // 如果文件不以换行结尾,最后一行也算
    if (count > 0 || !feof(fp)) {
        count++;
    }
    
    fclose(fp);
    return count;
}

int main() {
    int lines = countLines("example.txt");
    if (lines >= 0) {
        printf("文件共有%d行\\n", lines);
    }
    
    return 0;
}
```

### 练习3:学生信息管理系统(带文件存储)

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

#define MAX_STUDENTS 100

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

void saveStudents(Student students[], int count) {
    FILE *fp = fopen("students.dat", "wb");
    if (fp == NULL) {
        perror("保存失败");
        return;
    }
    
    fwrite(students, sizeof(Student), count, fp);
    fclose(fp);
    printf("保存成功!\\n");
}

int loadStudents(Student students[]) {
    FILE *fp = fopen("students.dat", "rb");
    if (fp == NULL) {
        return 0;
    }
    
    int count = 0;
    while (fread(&students[count], sizeof(Student), 1, fp) == 1) {
        count++;
    }
    
    fclose(fp);
    return count;
}

int main() {
    Student students[MAX_STUDENTS];
    int count = loadStudents(students);
    
    // 添加新学生
    strcpy(students[count].name, "新学生");
    students[count].id = count + 1;
    students[count].score = 85.5f;
    count++;
    
    saveStudents(students, count);
    
    return 0;
}
```

## 九、总结与延伸

### 本节重点回顾

1. **文件类型**:文本文件和二进制文件
2. **文件指针**:`FILE *`类型
3. **文件打开**:`fopen(filename, mode)`
4. **文件关闭**:`fclose(fp)`
5. **字符读写**:`fgetc`和`fputc`
6. **字符串读写**:`fgets`和`fputs`
7. **格式化读写**:`fscanf`和`fprintf`
8. **二进制读写**:`fread`和`fwrite`
9. **文件定位**:`fseek`、`ftell`、`rewind`
10. **错误处理**:检查返回值、使用`perror`

### 下节预告

下一篇文章我们将学习《综合实战:实现一个简易计算器》,将前面所学知识综合应用。

📌 **知识点卡片**
– 文件类型:文本文件(可读)、二进制文件(高效)
– 文件指针:`FILE *fp`
– 打开模式:`r`, `w`, `a`, `rb`, `wb`, `ab`等
– 字符读写:`fgetc`, `fputc`
– 字符串读写:`fgets`, `fputs`
– 格式化读写:`fscanf`, `fprintf`
– 二进制读写:`fread`, `fwrite`
– 文件定位:`fseek(fp, offset, SEEK_SET/SEEK_CUR/SEEK_END)`

💡 **小技巧**
– 始终检查`fopen`的返回值
– 成对使用`fopen`和`fclose`
– 使用`perror`打印错误信息
– 二进制文件适合存储结构体数据
– 使用`feof`判断文件是否结束

⚠️ **注意事项**
– 文件操作完成后必须关闭文件
– 注意文件路径的正确性
– 二进制文件和文本文件不能混用
– `fgets`会读取换行符,需要注意处理

> 如果你对文件操作有任何疑问,欢迎在评论区留言讨论!下一篇见!
 

 

赞(0)
未经允许不得转载:171主机测评 » 13-文件操作:数据的持久化存储
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址