使用 fprintf 和 fscanf (文本格式)
这是最直观的方法,将数组元素以人类可读的文本形式(用空格或换行符分隔)写入文件。
优点:
- 可读,可以用记事本等工具直接打开查看。
- 调试方便。
缺点:
- 占用空间可能比二进制格式大。
- 读写速度相对较慢,因为涉及到文本格式转换。
- 如果数组元素本身包含空格或特殊字符,可能会被错误解析。
保存数组到文件 (fprintf)
#include <stdio.h>
int main() {
int numbers[] = {10, 20, 30, 40, 50};
int size = sizeof(numbers) / sizeof(numbers[0]);
// 1. 打开文件 (w: 写模式,如果文件存在则覆盖)
FILE *file = fopen("array_data.txt", "w");
if (file == NULL) {
perror("无法打开文件进行写入");
return 1;
}
// 2. 将数组大小写入文件,方便后续读取
fprintf(file, "%d\n", size);
// 3. 循环写入数组元素
for (int i = 0; i < size; i++) {
fprintf(file, "%d ", numbers[i]); // 用空格分隔
// 或者用换行符 fprintf(file, "%d\n", numbers[i]);
}
// 4. 关闭文件
fclose(file);
printf("数组已成功保存到 array_data.txt\n");
return 0;
}
从文件读取数组 (fscanf)
读取时,需要先读取数组的大小,然后循环读取相应数量的元素。
#include <stdio.h>
int main() {
int size;
int read_numbers[100]; // 假设数组最大为100
// 1. 打开文件 (r: 读模式)
FILE *file = fopen("array_data.txt", "r");
if (file == NULL) {
perror("无法打开文件进行读取");
return 1;
}
// 2. 先读取数组大小
if (fscanf(file, "%d", &size) != 1) {
printf("读取数组大小失败\n");
fclose(file);
return 1;
}
// 3. 循环读取数组元素
for (int i = 0; i < size; i++) {
if (fscanf(file, "%d", &read_numbers[i]) != 1) {
printf("读取第 %d 个元素失败\n", i);
break; // 提前终止循环
}
}
// 4. 关闭文件
fclose(file);
// 5. 验证读取结果
printf("从文件中读取的数组大小为: %d\n", size);
printf("数组内容为: ");
for (int i = 0; i < size; i++) {
printf("%d ", read_numbers[i]);
}
printf("\n");
return 0;
}
使用 fwrite 和 fread (二进制格式)
这种方法直接将数组在内存中的二进制数据块原封不动地写入文件,这是最高效、最节省空间的方法。
优点:
- 读写速度极快。
- 文件体积小,没有格式开销。
- 可以处理任何类型的数据(结构体、浮点数等),只要保证写入和读取时的数据类型一致。
缺点:
- 是二进制码,无法直接用文本编辑器阅读,可读性差。
保存数组到文件 (fwrite)
#include <stdio.h>
int main() {
int numbers[] = {10, 20, 30, 40, 50};
int size = sizeof(numbers) / sizeof(numbers[0]);
// 1. 打开文件 (wb: 二进制写模式)
FILE *file = fopen("array_data.bin", "wb");
if (file == NULL) {
perror("无法打开文件进行二进制写入");
return 1;
}
// 2. 将整个数组一次性写入文件
// fwrite(数组地址, 每个元素的大小, 元素个数, 文件指针)
size_t elements_written = fwrite(numbers, sizeof(int), size, file);
if (elements_written != size) {
printf("写入元素数量不匹配!\n");
}
// 3. 关闭文件
fclose(file);
printf("数组已成功以二进制格式保存到 array_data.bin\n");
return 0;
}
从文件读取数组 (fread)
读取时,同样是一次性读取整个数据块到内存中。
#include <stdio.h>
int main() {
int read_numbers[100]; // 必须有足够大的空间来存放读取的数据
int size;
// 1. 打开文件 (rb: 二进制读模式)
FILE *file = fopen("array_data.bin", "rb");
if (file == NULL) {
perror("无法打开文件进行二进制读取");
return 1;
}
// 2. 一次性读取整个数组
// fread(存放地址, 每个元素的大小, 元素个数, 文件指针)
size_t elements_read = fread(read_numbers, sizeof(int), 5, file); // 我们知道原数组有5个元素
// 获取实际读取的元素个数
size = elements_read;
// 3. 关闭文件
fclose(file);
// 4. 验证读取结果
printf("从二进制文件中读取的数组大小为: %zu\n", elements_read);
printf("数组内容为: ");
for (int i = 0; i < size; i++) {
printf("%d ", read_numbers[i]);
}
printf("\n");
return 0;
}
注意: 使用 fread 时,你需要预先知道要读取多少个元素(你可以在文件开头单独写入一个 size 变量)。
混合方法(推荐)
这是最健壮的方法,结合了两种方式的优点:先写入数组的大小,再以二进制格式写入整个数组。
这样,读取文件时,第一步就能知道要读取多少个数据,避免了缓冲区溢出或读取不完整的问题。
保存(写入大小 + 二进制数据)
#include <stdio.h>
int main() {
int numbers[] = {10, 20, 30, 40, 50};
int size = sizeof(numbers) / sizeof(numbers[0]);
FILE *file = fopen("array_data_mixed.bin", "wb");
if (file == NULL) {
perror("无法打开文件");
return 1;
}
// 1. 先写入数组大小
fwrite(&size, sizeof(int), 1, file);
// 2. 再写入数组数据
fwrite(numbers, sizeof(int), size, file);
fclose(file);
printf("数组已成功以混合格式保存到 array_data_mixed.bin\n");
return 0;
}
读取(先读大小,再读数据)
#include <stdio.h>
int main() {
int read_numbers[100];
int size;
FILE *file = fopen("array_data_mixed.bin", "rb");
if (file == NULL) {
perror("无法打开文件");
return 1;
}
// 1. 先读取数组大小
if (fread(&size, sizeof(int), 1, file) != 1) {
printf("读取数组大小失败\n");
fclose(file);
return 1;
}
// 2. 检查大小是否在安全范围内
if (size > 100) {
printf("错误:文件中的数组大小超过了缓冲区容量!\n");
fclose(file);
return 1;
}
// 3. 读取数组数据
size_t elements_read = fread(read_numbers, sizeof(int), size, file);
if (elements_read != size) {
printf("警告:读取的元素数量与文件头声明的不一致,\n");
}
fclose(file);
// 4. 验证
printf("从混合文件中读取的数组大小为: %d\n", size);
printf("数组内容为: ");
for (int i = 0; i < size; i++) {
printf("%d ", read_numbers[i]);
}
printf("\n");
return 0;
}
总结与对比
| 特性 | fprintf / fscanf (文本) |
fwrite / fread (二进制) |
混合方法 (推荐) |
|---|---|---|---|
| 可读性 | 高,可手动编辑 | 低,二进制乱码 | 低,文件头可读 |
| 文件大小 | 较大 | 小 | 较小 |
| 读写速度 | 较慢 | 快 | 快 |
| 数据安全性 | 一般,易受格式影响 | 高,原样读写 | 高,有长度校验 |
| 适用场景 | 配置文件、需要人工查看的数据 | 大数据量、性能敏感的场景(如游戏存档、科学计算) | 绝大多数情况下的最佳选择,兼顾了安全性和效率 |
核心建议:
- 如果数据需要被人类阅读或编辑,使用方法一。
- 如果追求极致的性能和最小的文件体积,并且不关心文件的可读性,使用方法二。
- 对于绝大多数程序内部的数据持久化需求,方法三(混合方法)是最安全、最可靠的选择。
