<stdio.h>>
Section outline
-
<stdio.h>– 输入/输出(Input/Output)
文件与文件系统(Files and File Systems)
文件和目录系统的许多方面是实现相关的,甚至连“文件名”这种最基本的概念,标准 C 都无法统一规范。
-
实现必须支持哪些格式的文件名?
-
目录和设备名的规则也没有统一标准。
-
虽然标准头文件名是固定的,但这些名称不一定与文件系统中的实际文件名完全一致。
一些实现可能允许文件名包含通配符,例如
*.dat表示所有以.dat为扩展名的文件。但标准 I/O 函数并不要求支持通配符功能。操作系统可能会对每个用户限制最大打开文件数。有些文件系统不允许同一目录中存在多个同名文件,这对使用
"w"模式的fopen可能造成影响。一些文件系统对用户设置磁盘配额,如果文件过大可能导致写入失败,直到写操作失败时才会察觉。
经研究发现,为实现可移植性,标准 C 认为“安全”的文件名格式是:最多六个字母 + 一个点 + 最多一个字母的扩展名,并建议统一大小写。但你也可以用条件编译来适配不同平台。
命令行重定向的行为(如
stdin重定向为文件)是实现相关的。例如,gets和fgets(stdin)行为略有不同,但如果stdin被重定向,gets实际上也可能从文件读取。文件缓冲、磁盘扇区大小等也都是实现相关的。但标准 C 要求实现至少能够处理每行包含 254 个字符(包括换行符)的文本文件。
stdin,stdout,stderr有时由操作系统维护,有时在程序启动时创建。这三者是否计入最大打开文件数由实现决定。下列宏的值为实现定义:
-
BUFSIZ:默认缓冲区大小 -
FOPEN_MAX:允许同时打开的最大文件数 -
FILENAME_MAX:合法文件名最大长度 -
TMP_MAX:可生成的唯一临时文件名最大数
参见: C11 引入的“边界检查接口”附录可能对该头文件提出扩展要求。
C++ 注意事项:对应标准 C++ 头文件为
<cstdio>。
文件操作函数(Operations on Files)
remove函数-
通常会真正删除文件。
-
但如果该文件名只是某个文件的“别名”(例如硬链接),则只有最后一个别名被删除时,文件才会被真正删除。
-
如果删除的文件正在被打开使用,行为是实现定义的。
rename函数-
旧文件名会被删除,类似调用了
remove。 -
如果旧文件正在被打开使用,行为是实现定义的。
-
如果新文件名已存在,行为同样实现定义。
-
在某些文件系统中,不能跨目录重命名,可能会失败,也可能实际是执行复制+删除。
tmpfile函数-
如果程序异常终止,临时文件可能不会被删除。
-
临时文件的目录、权限、文件名等都是实现定义的。
tmpnam函数-
超过
TMP_MAX次调用时,行为是实现定义的。 -
若提供的非 NULL 地址指向的缓冲区小于
L_tmpnam字符,行为未定义。 -
返回的文件名在调用时是唯一的,但在使用前可能已被其他程序创建,若需避免此问题,应使用
tmpfile。
文件访问函数(File Access Functions)
fclose-
程序异常终止时,不能保证输出流会自动刷新写入缓冲区。
-
在某些实现中,若未写入内容可能无法成功关闭并保留空文件。
fflush-
如果是输入流,或上次操作非输出,则行为未定义。但一些实现允许安全地刷新输入流。
-
stdout,stderr允许调用fflush。 -
stdin上的fflush是未定义行为,但一些实现允许这么做。
fopen-
一些实现可能在文本文件上无法执行
seek,此时+模式可能意味着必须加上b。 -
'w'模式下,如果文件已存在,有些系统是覆盖,有些则是创建新版本。 -
模式字符含义是实现定义的。C11 增加了独占模式
'x'。 -
二进制文件关闭时,有些系统会在末尾添加
\0,之后以追加方式打开时可能从文件尾之后开始写入。 -
只有当实现确定打开的是非交互设备,才使用全缓冲。
-
成功时返回
FILE*,失败返回 NULL。标准 C 不保证设置errno。
freopen-
成功时返回
stream,失败返回 NULL。是否设置errno未定义。
setbuf-
无返回值,程序员需确保传入的是已打开文件流,缓冲区为 NULL 或足够大。
-
实现可将不同缓冲类型视作等效。即使设置了缓冲方式,也可能无效,且不会有错误提示。
setvbuf-
mode可为:_IOFBF(全缓冲)、_IOLBF(行缓冲)、_IONBF(无缓冲)。 -
返回 0 表示成功,非 0 表示失败(例如
mode无效),不保证设置errno。 -
若程序员提供了缓冲区,该缓冲区在流关闭前必须保持有效。
-
实现可能不会真正使用提供的缓冲区。
-
缓冲区大小是实现定义的。
格式化输入/输出函数(Formatted Input/Output Functions)
以下函数的行为,均共享
fprintf/fscanf所定义的通用格式规则。fprintf-
若参数不足,行为未定义。
-
C89 增加了
i,n,p转换符(p以实现定义格式输出void*)。 -
C99 增加了
F,a,A转换符,以及hh,ll,j,t,z长度修饰符;支持无穷大和 NaN。 -
无效转换符行为未定义,标准保留未使用的小写符号供将来使用。
-
若参数是结构体、联合体或数组(除
%s和%p允许的情况),行为未定义。 -
没有原型声明时调用该函数,行为未定义。
-
格式化输出缓存有限,标准要求至少支持单项转换为 509 字符。
fscanf-
输入行为与
fscanf类似,错误返回EOF,不保证设置errno。 -
p要求参数为void*,格式为实现定义。
其他格式化函数(所有以下函数在 C99 引入)
输出类函数(遵循
fprintf行为):-
printf,snprintf,sprintf,vfprintf,vprintf,vsnprintf,vsprintf
输入类函数(遵循
fscanf行为):-
scanf,sscanf,vfscanf,vscanf,vsscanf
字符输入输出函数(Character I/O Functions)
gets-
在 C11 中被移除,因为其存在安全隐患(缓冲区溢出风险)。
ungetc-
C99 弃用了在二进制文件开头使用该函数的行为。
直接输入输出函数(Direct I/O Functions)
fread-
发生错误时,文件位置指示器的值为不确定。
-
若只读取部分字段,该字段值为不确定。
-
标准未规定是否将 CR/LF 对在读取时转换为换行符,某些实现对文本文件会这样处理。
fwrite-
同样,发生错误时,文件位置指示器为不确定。
-
输出时是否将换行符转换为 CR/LF 对也是实现定义的。
文件定位函数(File Positioning Functions)
fgetpos/fsetpos-
两者均由 C89 引入。
-
失败时返回非零,并设置
errno为实现定义的正值。
错误处理函数(Error-Handling Functions)
perror-
错误信息的内容和格式均为实现定义。
-