核心内容摘要
18岁的成年礼:一场关于告别与启航的诗意叙事
【Linux 封神之路】文件操作 时间编程实战从缓冲区到时间格式化全解析大家好我是专注 Linux 技术分享的小杨。
上一篇给大家整理了系统监控与性能分析工具解决了服务器卡慢、程序崩溃的问题。
今天接着 Linux 开发核心技能系列聚焦 “文件操作” 和 “时间编程”—— 这两个是嵌入式 Linux 开发的高频需求比如日志写入、配置文件读写、时间戳记录等场景都离不开。
结合文件操作及时间编程资料从 “缓冲区 / 非缓冲区文件操作” 到 “时间格式转换”手把手教你实战用法附完整代码示例新手直接套用
先理清文件操作的两种核心方式Linux 文件操作分为 “基于缓冲区” 和 “基于非缓冲区” 两种适用场景不同核心区别在于是否通过标准库缓冲区优化 IO 效率基于缓冲区fopen/fwrite/fread标准库封装自动缓冲数据减少系统调用次数适合普通文件读写如文本文件、配置文件基于非缓冲区open/write/read直接调用系统调用无中间缓冲实时性高适合设备文件操作如串口、磁盘、嵌入式实时场景。
下面分别拆解两种方式的核心函数和实战代码。
实战 1基于缓冲区的文件操作标准库函数基于缓冲区的文件操作通过stdio.h头文件的函数实现用法简单、兼容性好是开发中的首选。
核心函数详解按操作流程函数功能核心参数 / 返回值fopen打开 / 创建文件参数 1文件路径如test.txt参数 2打开模式r读、w写、a追加、r读写返回值文件指针FILE *失败返回NULLfwrite写入数据参数 1数据缓冲区地址参数 2单个数据大小参数 3数据个数参数 4文件指针返回值成功写入的个数fprintf格式化写入文本文件类似printf多一个文件指针参数如fprintf(fp, name:%s\n, Linux)fread读取数据参数 1接收数据的缓冲区参数 2单个数据大小参数 3读取个数参数 4文件指针返回值成功读取的个数fscanf格式化读取文本文件类似scanf多一个文件指针参数如fscanf(fp, %d, num)fseek移动文件光标参数 1文件指针参数 2偏移量正值向右、负值向左参数 3基准位置SEEK_SET文件开头、SEEK_CUR当前位置、SEEK_END文件末尾fclose关闭文件参数文件指针返回值0 成功-1 失败必须关闭避免资源泄漏
实战代码文本文件读写日志记录场景c运行#include stdio.h #include string.h int main() { //
打开文件不存在则创建存在则追加写入 FILE *fp fopen(app.log, a); if (fp NULL) { perror(fopen failed); return 1; } //
格式化写入数据模拟日志时间内容 char log_msg[]
16:00:00 [INFO] 程序启动成功\n; fprintf(fp, %s, log_msg); //
移动光标到文件开头读取所有日志 fseek(fp, 0, SEEK_SET); char buf[1024]; printf(日志内容\n); while (fgets(buf, sizeof(buf), fp) ! NULL) { // 逐行读取 printf(%s, buf); } //
关闭文件 fclose(fp); fp NULL; // 避免野指针 return 0; }
关键
注意事项打开模式选择w会清空文件原有内容a会在文件末尾追加r支持读写但不会创建文件必须检查fopen返回值文件不存在、权限不足都会导致打开失败写完数据后若需立即读取需用fseek移动光标否则光标在文件末尾读取不到数据。
实战 2基于非缓冲区的文件操作系统调用非缓冲区文件操作通过系统调用实现无中间缓冲IO 响应更快适合嵌入式实时场景如串口通信、磁盘 IO。
核心函数详解按操作流程函数功能核心参数 / 返回值open打开 / 创建文件参数 1文件路径参数 2打开标志必选O_RDONLY只读、O_WRONLY只写、O_RDWR读写可选O_CREAT创建、O_APPEND追加、O_TRUNC清空参数 3文件权限O_CREAT时必填如0644rw-r--r--返回值文件描述符int失败返回 - 1write写入数据参数 1文件描述符open 返回值参数 2数据缓冲区参数 3数据长度返回值成功写入的字节数失败返回 - 1read读取数据参数 1文件描述符参数 2接收缓冲区参数 3读取长度返回值成功读取的字节数0 表示文件结束失败返回 - 1lseek移动光标参数 1文件描述符参数 2偏移量参数 3基准位置SEEK_SET/SEEK_CUR/SEEK_END返回值光标位置的字节数失败返回 - 1close关闭文件参数文件描述符返回值0 成功-1 失败
实战代码二进制文件读写数据存储场景c运行#include stdio.h #include sys/types.h #include sys/stat.h #include fcntl.h #include unistd.h #include string.h // 定义要存储的结构体模拟传感器数据 typedef struct { int temp; // 温度 int humidity;// 湿度 long time; // 时间戳 } SensorData; int main() { //
打开文件创建读写清空原有内容权限0644 int fd open(sensor.bin, O_RDWR | O_CREAT | O_TRUNC,
; if (fd -
{ perror(open failed); return 1; } //
准备数据并写入二进制格式 SensorData data {25, 60, 1738064000}; ssize_t write_len write(fd, data, sizeof(SensorData)); if (write_len ! sizeof(SensorData)) { perror(write failed); close(fd); return 1; } printf(写入二进制数据成功长度%ld字节\n, write_len); //
移动光标到文件开头读取数据 lseek(fd, 0, SEEK_SET); SensorData read_data; ssize_t read_len read(fd, read_data, sizeof(SensorData)); if (read_len -
{ perror(read failed); close(fd); return 1; } printf(读取数据温度%d℃湿度%d%%时间戳%ld\n, read_data.temp, read_data.humidity, read_data.time); //
关闭文件 close(fd); return 0; }
关键
注意事项文件权限0644表示所有者可读可写组用户和其他用户只读嵌入式开发中常用权限文件描述符open返回的是整数如
4而非FILE *需注意与缓冲区操作的区别无缓冲特性write调用后数据直接写入文件或设备无需刷新缓冲区实时性更高。
实战 3时间编程时间戳 格式化转换时间编程在日志记录、数据同步、定时任务中必不可少核心是 “时间戳→时间结构体→格式化字符串” 的转换流程。
核心概念与结构体时间戳从 1970 年 1 月 1 日 0 时 0 分 0 秒UTC到当前时间的秒数time_t类型时间结构体struct tm拆分后的时间格式包含秒、分、时、日、月、年等字段注意tm_mon从 0 开始tm_year 实际年份 - 1900。
核心函数详解按转换流程函数功能核心参数 / 返回值time获取当前时间戳参数保存时间戳的地址可传NULL直接返回返回值当前时间戳失败返回 - 1localtime时间戳→本地时间结构体参数时间戳指针返回值struct tm *本地时间如北京时间gmtime时间戳→格林威治时间结构体参数时间戳指针返回值struct tm *UTC 时间比北京时间晚 8 小时ctime时间戳→默认格式字符串参数时间戳指针返回值字符串指针如Wed Jan 28 16:30:00 2026\nasctime时间结构体→默认格式字符串参数struct tm *返回值字符串指针格式同ctimestrftime时间结构体→自定义格式字符串参数 1结果缓冲区参数 2缓冲区大小参数 3格式字符串如%Y-%m-%d %H:%M:%S参数 4时间结构体返回值成功转换的字符数
实战代码时间格式化日志时间戳场景c运行#include stdio.h #include time.h #include string.h int main() { //
获取当前时间戳 time_t now time(NULL); if (now -
{ perror(time failed); return 1; } printf(当前时间戳%ld\n, now); //
时间戳→本地时间结构体 struct tm *local_tm localtime(now); if (local_tm NULL) { perror(localtime failed); return 1; } //
自定义格式化时间常用格式年-月-日 时:分:秒 char time_str[64]; strftime(time_str, sizeof(time_str), %Y-%m-%d %H:%M:%S, local_tm); printf(自定义格式时间%s\n, time_str); //
时间戳→默认格式字符串 char *default_time ctime(now); printf(默认格式时间%s, default_time); //
时间结构体→默认格式字符串 char *asctime_str asctime(local_tm); printf(asctime格式时间%s, asctime_str); return 0; }
常用时间格式符strftime核心格式符含义示例%Y4 位年份2026%m2 位月份
%d2 位日期
%H24 小时制小时
%M分钟
%S秒
%w星期
为周日3%j一年中的第几天
8
综合实战文件操作 时间编程日志系统结合前面的知识点实现一个带时间戳的日志系统自动记录程序运行状态c运行#include stdio.h #include time.h #include string.h // 日志写入函数带时间戳 void write_log(const char *level, const char *msg) { //
打开日志文件追加模式 FILE *fp fopen(system.log, a); if (fp NULL) { perror(fopen log failed); return; } //
获取当前时间并格式化 time_t now time(NULL); struct tm *local_tm localtime(now); char time_str[64]; strftime(time_str, sizeof(time_str), %Y-%m-%d %H:%M:%S, local_tm); //
写入日志格式时间 级别 消息 fprintf(fp, [%s] [%s] %s\n, time_str, level, msg); //
关闭文件 fclose(fp); fp NULL; } int main() { write_log(INFO, 程序启动); write_log(INFO, 开始执行数据采集); write_log(WARNING, 传感器数据波动较大); write_log(INFO, 程序执行完成); printf(日志写入成功查看system.log文件\n); return 0; }运行后查看system.log文件输出如下plaintext[
17:00:00] [INFO] 程序启动 [
17:00:01] [INFO] 开始执行数据采集 [
17:00:05] [WARNING] 传感器数据波动较大 [
17:00:10] [INFO] 程序执行完成
避坑指南常见错误解决文件操作失败权限不足原因创建文件时未指定权限open用O_CREAT但未传mode参数或目录无写权限解决open函数添加权限参数如0644或用sudo运行程序仅测试场景。
时间格式化出现乱码原因strftime的缓冲区大小不足或格式符错误解决增大缓冲区如char time_str[64]核对格式符如%Y而非%y。
非缓冲区操作读取不到数据原因read后未判断返回值或光标位置错误解决用lseek移动光标到正确位置检查read返回值0 表示文件结束-1 表示失败。
七、
总结核心技能与应用场景文件操作普通文本 / 配置文件用缓冲区操作fopen/fprintf/fscanf效率更高设备文件 / 实时场景用非缓冲区操作open/write/read实时性更强时间编程日志记录用strftime自定义时间格式搭配文件操作实现带时间戳日志数据同步用time获取时间戳实现跨设备时间同步嵌入式适配权限控制嵌入式设备中文件权限需严格配置如0644避免权限过高资源释放文件操作后必须关闭fclose/close避免资源泄漏。
掌握文件操作和时间编程能应对嵌入式开发中 80% 的 IO 场景日志、配置、数据存储。
下一篇博客我会给大家整理 “Linux 多线程编程”解决并发执行、资源同步等问题敬请关注