2.9 临时文件
应用常常需要使用临时文件来暂存数据,例如,编译程序在编译器和汇编器之间交换编译结果就常常通过临时文件来进行。这种临时文件有两个特点:允许其他用户或进程读写并且名字必须唯一。前一特点要求将它们保存在公共目录中,以便任何用户读写,UNIX系统为此提供了一个专用的公共目录/tmp。后一特点是为了防止其他进程在相同目录中创建同名文件而引起冲突。UNIX系统给应用提供了专门的临时文件命名和临时文件创建函数,以保证临时文件名的唯一性。
唯一的文件名可以通过函数tmpnam()和tempnam()而获得:
#include <stdio.h>
char tmpnam(char s);
char tempnam(const char dir, const char *pfx);
tmpnam()返回一个合法的临时文件名,此文件名的目录由中定义的宏变量P_tmpdir指定,P_tmpdir通常就是/tmp。如果参数s不是空指针,tmpnam()存储该临时文件名于s指出的字符串中,此时s指向的字符串大小应当至少不小于宏常数L_tmpnam。若s为空指针,返回值指向的字符串是由tmpnam()静态分配的,将被下一次调用tmpnam()所覆盖。在同一个进程内,tmpnam()至多可以调用TMP_MAX次,并且每一次调用生成的临时文件名各不相同。
tempnam()的功能比tmpnam()的功能还要多一点,因此名字中多一个字母“e”。它可以指定临时文件存放的目录以及文件名的前缀。dir参数给出目录的路径,pfx参数给出文件名前缀。
tempnam()依次测试下述条件来确定目录路径名:
如果定义了环境变量TMPDIR,用它的值作为目录,即它可以覆盖dir参数。
如果dir参数指向一个合法的目录字符串,用它作为目录。
如果dir参数是空指针或者指向一个非法目录路径名,使用宏变量P_tmpdir 定义的目录。
如果P_tmpdir 定义的目录不可访问,使用实现定义的目录,通常是“/tmp”。
有许多应用喜欢以某种开头字符序列来命名临时文件名,pfx参数较适合这些应用。这个参数可以是空指针,也可以指向至多5个字符的一个字符串。
tempnam()存放文件名的存储空间是用malloc()分配的,当不再需要时,应当调用free()来释放它们。
tmpnam()和tempnam()仅仅根据系统当时的情况命名一个新的临时文件名,并不实际创建文件。创建和删除临时文件都是应用自己的事情。在获得文件名和文件被实际创建的这段时间内,其他进程有可能会创建同名文件,因此不建议用它们。
函数tmpfile()能避免这种竞争条件的出现,它在命名临时文件的同时也打开它。
#include <stdio.h>
FILE *tmpfile(void);
tmpfile()以读/写方式(w+)创建一个唯一的临时文件,并返回文件指针。该临时文件在进程结束时将自动删除。C标准没有规定文件的目录,Linux默认情况下为/tmp,否则为宏变量P_tmpdir指定的目录。
例2-10 程序2-10展示了上述三个函数的用法。它先调用tmpnam(),然后用存在和不存在的路径名分别调用tempname()来查看这两个函数生成的临时文件名。最后,调用tmpfile()创建一个临时文件并向其中写入一行数据,然后反绕这个文件将数据输出到标准输出验证这个临时文件的存在。
程序2-10 创建临时文件之例
#include "ch02.h"
int main(void)
{
FILE *tempfp;
char tmpname[L_tmpnam],*cp;
char line[256];
char *pfx = "XXXXX";
if ((cp=tmpnam(tmpname))==NULL) // 查看tmpnam()生成的文件名
printf("a unique name cannot be generate by tmpnam()\n");
else
printf(" file name get by tmpnam(): %s\n", cp);
/* 查看tempnam()生成的文件名 */
cp = tempnam("./", pfx);
printf(" by tempnam(\"./\",pfx): %s\n", cp);
free(cp);
cp = tempnam("/not_exist_dir/", pfx);
printf("by tempnam(\"/not_exist_dir\",pfx): %s\n", cp);
free(cp);
/* 用tmpfile()创建一个临时文件 */
if ((tempfp=tmpfile()) == NULL)
err_exit("tmpfile error");
printf("tmpfile() created a temporary file\n");
/* 向临时文件写入一行数据,然后将它读出并写至标准输出进行验证 */
fputs("One line of output\n", tempfp);
rewind(tempfp);
if(fgets(line, sizeof(line), tempfp) == NULL)
err_exit("fgets error");
fputs(line,stdout);
exit(0);
}