4、创建第一个日志文件
好,咱们现在开始动手写代码。
前面讲了那么多理论,什么文件系统、什么日志等级,说实话,不落地都是空的。我个人习惯是,先让代码跑起来,哪怕只是往文件里写一个"Hello World",也比看十页文档强。
4.1 核心思路:打开、写入、关闭
创建日志文件这件事,说白了就三步:
- 打开文件——告诉系统我要用哪个文件
- 写入数据——把字符串塞进去
- 关闭文件——告诉系统我用完了
你想想看,这跟你在记事本里写东西有什么区别?没区别。只不过咱们要用C语言来指挥单片机干这事。
4.2 代码示例:第一个日志文件
下面这段代码,我建议你亲手敲一遍。别复制,敲一遍印象深。
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *log_file;
const char *filename = "irrigation.log";
// 第一步:打开文件
log_file = fopen(filename, "w");
if (log_file == NULL) {
printf("错误:无法创建日志文件 %s\n", filename);
return -1;
}
// 第二步:写入固定字符串
fprintf(log_file, "灌溉控制器日志系统启动\n");
fprintf(log_file, "时间:2024-01-15 08:30:00\n");
fprintf(log_file, "状态:初始化完成\n");
// 第三步:关闭文件
fclose(log_file);
printf("日志文件创建成功:%s\n", filename);
return 0;
}
关键点解析:
fopen(filename, "w")——"w"表示写入模式,如果文件不存在就创建,存在就覆盖fprintf()——跟printf用法一样,只是多了一个文件指针参数fclose()——一定要关!不关文件,数据可能没真正写进去
4.3 验证文件是否创建成功
代码跑完了,怎么知道文件真的创建了?
我一般用两种方法验证:
- 看返回值——fopen返回非NULL,说明成功了
- 看文件系统——在电脑上找到那个文件,双击打开看看
在嵌入式系统里,我习惯再加一步:用fseek和ftell检查文件大小。
// 验证文件大小
fseek(log_file, 0, SEEK_END);
long file_size = ftell(log_file);
printf("文件大小:%ld 字节\n", file_size);
如果文件大小是0,那说明写入可能没成功。嗯,这里要注意,有时候fprintf返回了正数,但数据还在缓冲区里没刷到磁盘上。
4.4 处理文件打开失败的错误
我曾经在一个项目里吃过这个亏。SD卡没插好,程序直接跑飞了,查了半天才发现是fopen返回了NULL,但代码里没做任何处理。
从那以后,我写文件操作必做三件事:
- 检查fopen返回值——NULL就是失败
- 打印错误原因——用
perror()或strerror(errno) - 优雅退出——别让系统卡死,该释放的资源要释放
常见失败原因:
| 错误原因 | 典型场景 |
|---|---|
| 路径不存在 | SD卡没插,或者目录没创建 |
| 权限不足 | 文件系统被写保护了 |
| 磁盘已满 | 存储空间用完了 |
| 文件被占用 | 另一个任务正在读写同一个文件 |
我的小技巧:
在正式写入日志之前,先尝试创建一个测试文件。如果连测试文件都创建不了,那说明存储系统有问题,早点报错比写到一半崩溃强。
4.5 完整示例:带错误处理的日志创建
#include <stdio.h>
#include <errno.h>
#include <string.h>
int create_log_file(const char *filename) {
FILE *fp = fopen(filename, "w");
if (fp == NULL) {
printf("打开文件失败:%s\n", strerror(errno));
return -1;
}
fprintf(fp, "=== 灌溉控制器日志 ===\n");
fprintf(fp, "启动时间:2024-01-15 08:30:00\n");
fprintf(fp, "固件版本:v2.1.3\n");
fclose(fp);
return 0;
}
int main() {
if (create_log_file("irrigation.log") == 0) {
printf("日志文件创建成功\n");
} else {
printf("日志文件创建失败,请检查存储设备\n");
}
return 0;
}
这段代码我用了strerror(errno)来打印具体的错误信息。为什么这么做?因为"打开文件失败"太笼统了,你得告诉用户到底是磁盘满了还是路径不对。
4.6 总结一下
创建第一个日志文件,其实就三行核心代码:fopen、fprintf、fclose。但真正工程化的代码,得加上错误处理、文件验证、资源释放。
我见过太多新手写代码只关注"正常流程",忽略了"异常流程"。记住一句话:在嵌入式系统里,异常才是常态。SD卡会松、Flash会坏、电源会掉,你的代码得扛得住这些。
下一节,咱们聊聊怎么往日志文件里追加内容,而不是每次都覆盖。