<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
-
错误信息的内容和格式均为实现定义。
-