12、临时文件操作:tmpfile函数、tmpnam函数、临时文件的创建与使用
做嵌入式开发这些年,我遇到过不少需要临时文件的场景。比如日志系统要暂存中间数据,或者某个模块需要交换大块内存放不下的信息。这时候,临时文件就是我们的好帮手。
说白了,临时文件就是“用完即弃”的文件。程序运行期间创建,退出时自动清理。C标准库提供了两个核心函数:tmpfile和tmpnam。它们各有各的脾气,用不对会踩坑。
12.1 tmpfile函数:最安全的临时文件
tmpfile是我个人最常用的临时文件函数。它直接创建一个临时文件,返回一个FILE*指针。文件以"wb+"模式打开,可读可写。
它的最大优点是:文件在关闭或程序退出时自动删除。你不需要手动调用remove,系统帮你擦屁股。
核心特点:
- 自动生成唯一文件名,不会冲突
- 以二进制读写模式打开("wb+")
- 关闭文件或程序退出时自动删除
- 返回NULL表示创建失败
来看一个实际例子。我在做传感器数据采集时,经常需要暂存一批数据再统一处理:
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *tmp = tmpfile();
if (tmp == NULL) {
perror("tmpfile failed");
return -1;
}
// 写入一些临时数据
int data[] = {23, 45, 67, 89, 12};
fwrite(data, sizeof(int), 5, tmp);
// 回到文件开头
rewind(tmp);
// 读出来验证
int buf[5];
fread(buf, sizeof(int), 5, tmp);
for (int i = 0; i < 5; i++) {
printf("%d ", buf[i]);
}
printf("\n");
// 关闭时自动删除
fclose(tmp);
return 0;
}
嗯,这里要注意:tmpfile创建的文件在磁盘上是有实体的,但你看不到它的名字。系统给它起了个随机名,存在临时目录里。你关掉文件指针,它就消失了。
我曾经踩过的坑:
在某个RTOS环境下,tmpfile返回了NULL。查了半天,发现是系统的临时目录空间满了。所以调用tmpfile后一定要检查返回值,别想当然认为一定成功。
12.2 tmpnam函数:只给你名字,不帮你干活
tmpnam和tmpfile不一样。它只生成一个唯一的文件名,不创建文件。你需要自己用fopen去打开它。
函数原型很简单:
char *tmpnam(char *s);
如果s不为NULL,文件名会复制到s指向的缓冲区(至少L_tmpnam个字节)。如果s为NULL,返回一个内部静态缓冲区的指针。
我的建议:
尽量用tmpfile而不是tmpnam。因为tmpnam只生成名字,从生成到打开之间有个时间窗口,别的进程可能用同样的名字创建了文件——这就是所谓的TOCTOU(Time of Check to Time of Use)问题。
但有些场景下,你确实需要自己控制文件名。比如你要把临时文件传给另一个模块,或者需要指定文件路径。这时候tmpnam就派上用场了:
#include <stdio.h>
int main() {
char filename[L_tmpnam];
if (tmpnam(filename) == NULL) {
fprintf(stderr, "tmpnam failed\n");
return -1;
}
printf("临时文件名: %s\n", filename);
// 自己创建并打开
FILE *fp = fopen(filename, "wb+");
if (fp == NULL) {
perror("fopen failed");
return -1;
}
// 使用文件...
fprintf(fp, "临时数据\n");
fclose(fp);
// 记得手动删除
remove(filename);
return 0;
}
你想想看,tmpnam生成的名称一般长这样:\s123.1或者fileXXXX。不同系统格式不同,但保证唯一。
12.3 临时文件的创建与使用:实战经验
在实际项目中,我总结了一套临时文件的使用套路。这里画个流程图,帮你理清思路:
流程其实不复杂。核心就两点:要不要控制文件名,以及记不记得清理。
12.4 避坑指南:我踩过的那些雷
做嵌入式开发十几年,临时文件这块我栽过好几次。分享几个典型问题:
问题1:tmpnam的缓冲区大小
我曾经在代码里写死了缓冲区为256字节,觉得肯定够用。结果在某个Unix系统上,临时目录路径特别长,文件名加上路径就超了。后来我改用L_tmpnam宏定义的大小,再也没出过问题。
问题2:忘记手动删除
用tmpnam创建的文件,程序退出后不会自动删除。我有一次在服务器上跑了个监控程序,每天生成几百个临时文件。一个月后磁盘满了,系统直接挂了。从那以后,我养成了习惯:用tmpnam必配remove,而且放在atexit里注册。
问题3:多线程安全
tmpnam如果传NULL,返回的是内部静态缓冲区。多线程环境下,这个缓冲区会被覆盖。我建议:多线程程序一律用tmpfile,或者给tmpnam传自己的缓冲区。
12.5 实际项目中的最佳实践
说了这么多,总结一下我个人在项目中的做法:
| 场景 | 推荐函数 | 理由 |
|---|---|---|
| 简单暂存数据,用完就扔 | tmpfile | 自动删除,省心安全 |
| 需要把文件名传给子进程 | tmpnam + fopen | 可以拿到文件名,但记得手动删 |
| 多线程环境 | tmpfile | 避免静态缓冲区竞争 |
| 嵌入式裸机/RTOS | 看系统支持 | 有些RTOS没有标准C库,得自己实现 |
最后说一句:临时文件虽好,但别滥用。能用内存解决的问题,尽量别碰磁盘。嵌入式设备Flash寿命有限,频繁写临时文件会加速老化。我一般只在数据量超过内存容量,或者需要跨进程传递数据时才用临时文件。
好了,临时文件这块就聊到这儿。记住核心原则:tmpfile省心但不可控,tmpnam灵活但麻烦。根据场景选对工具,比什么都重要。
公众号:蓝海资料掘金营,微信deep3321