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 验证文件是否创建成功

代码跑完了,怎么知道文件真的创建了?

我一般用两种方法验证:

  1. 看返回值——fopen返回非NULL,说明成功了
  2. 看文件系统——在电脑上找到那个文件,双击打开看看

在嵌入式系统里,我习惯再加一步:用fseekftell检查文件大小。

// 验证文件大小
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会坏、电源会掉,你的代码得扛得住这些。

下一节,咱们聊聊怎么往日志文件里追加内容,而不是每次都覆盖。