C 编程/stdio.h
C 编程语言为文件输入和输出提供了许多标准库函数。这些函数构成了 C 标准库头文件 <stdio.h>
的大部分内容。
C 的 I/O 功能按现代标准来说是相当底层的;C 将所有文件操作抽象成字节流操作,这些字节流可以是“输入流”或“输出流”。与一些早期的编程语言不同,C 不直接支持随机访问数据文件;要从文件中间的一个记录中读取数据,程序员必须创建一个流,定位到文件的中间,然后从流中按顺序读取字节。
流式文件 I/O 模型是由 Unix 操作系统推广的,该操作系统与 C 编程语言本身是同时开发的。绝大多数现代操作系统都从 Unix 中继承了流,并且 C 编程语言家族中的许多语言都继承了 C 的文件 I/O 接口,几乎没有或根本没有变化(例如,PHP)。C++ 标准库在其语法中反映了“流”的概念;参见 iostream。
大多数 C 文件输入/输出函数都在/stdio.h (/cstdio头文件中定义(在 C++ 中)。
- 文件访问
- fopen- 打开一个文件
- freopen- 用现有流打开另一个文件
- fflush- 将输出流与实际文件同步
- fclose- 关闭一个文件
- setbuf- 设置文件流的缓冲区
- setvbuf- 设置文件流的缓冲区及其大小
- fwide- 在宽字符 I/O 和窄字符 I/O 之间切换文件流
- 直接输入/输出
- fread- 从文件中读取
- fwrite- 写入文件
- 非格式化输入/输出
-
- 窄字符
- fgetc, getc- 从文件流中读取一个字符
- fgets- 从文件流中读取一个字符字符串
- fputc, putc- 将一个字符写入文件流
- fputs- 将一个字符字符串写入文件流
- getchar- 从 stdin 读取一个字符
- gets- 从 stdin 读取一个字符字符串
- putchar- 将一个字符写入 stdout
- puts- 将一个字符字符串写入 stdout
- ungetc- 将一个字符放回文件流
- 宽字符
- fgetwc, getwc- 从文件流中读取一个宽字符
- fgetws- 从文件流中读取一个宽字符字符串
- fputwc, putwc- 将一个宽字符写入文件流
- fputws- 将一个宽字符字符串写入文件流
- getwchar- 从 stdin 读取一个宽字符
- putwchar- 将一个宽字符写入 stdout
- ungetwc- 将一个宽字符放回文件流
- 格式化输入/输出
-
- 窄字符
- scanf, fscanf, sscanf- 从 stdin、文件流或缓冲区中读取格式化的输入
- vscanf, vfscanf, wsscanf- 使用可变参数列表从 stdin、文件流或缓冲区中读取格式化的输入
- printf, fprintf, sprintf, snprintf- 将格式化的输出打印到 stdout、文件流或缓冲区
- vprintf, vfprintf, vsprintf, vsnprintf- 使用可变参数列表将格式化的输出打印到 stdout、文件流或缓冲区
- 宽字符
- wscanf, fwscanf, swscanf- 从 stdin、文件流或缓冲区中读取格式化的宽字符输入
- vwscanf, vfwscanf, vswscanf- 使用可变参数列表从 stdin、文件流或缓冲区中读取格式化的宽字符输入
- wprintf, fwprintf, swprintf- 将格式化的宽字符输出打印到 stdout、文件流或缓冲区
- vwprintf, vfwprintf, vswprintf- 使用可变参数列表将格式化的宽字符输出打印到 stdout、文件流或缓冲区
- 文件定位
- ftell- 返回当前文件位置指示器
- fgetpos- 获取文件位置指示器
- fseek- 将文件位置指示器移动到文件中特定位置
- fsetpos- 将文件位置指示器移动到文件中特定位置
- rewind- 将文件位置指示器移动到文件开头
- 错误处理
- clearerr- 清除错误
- feof- 检查文件结束
- ferror- 检查文件错误
- perror- 将对应于当前错误的字符字符串显示到 stderr
- 文件操作
- remove- 删除一个文件
- rename- 重命名一个文件
- tmpfile- 返回指向临时文件的指针
- tmpnam- 返回一个唯一的文件名
fgetc
函数用于从流中读取一个字符。
int fgetc(FILE *fp);
如果成功,fgetc
返回流中的下一个字节或字符(取决于文件是“二进制”还是“文本”,如上文 fopen
中所述)。如果失败,fgetc 返回 EOF
。(可以使用 ferror
或 feof
与文件指针一起确定具体的错误类型。)
标准宏 getc
也在 <stdio.h>
中定义,其行为与 fgetc
几乎相同,只是它是一个宏,因此可能会多次评估其参数。
标准函数 getchar
也在 <stdio.h>
中定义,它不接受任何参数,等效于 getc(stdin)
。
使用 fgetc
、getc
或 getchar
时的一个错误是在将结果与 EOF
进行比较之前将其赋值给类型为 char
的变量。以下代码片段展示了这个错误,然后展示了正确的做法(使用类型 int)
错误 | 更正 |
---|---|
char c;
while ((c = getchar()) != EOF)
putchar(c);
|
int c; /* This will hold the EOF value */
while ((c = getchar()) != EOF)
putchar(c);
|
假设一个系统中 char
类型为 8 位宽,表示 256 个不同的值。getchar
可能返回 256 个可能的字符中的任何一个,它也可能返回 EOF
以指示文件结束,总共 257 个不同的返回值。
当 getchar
的结果被赋值给一个 char
时,它只能表示 256 个不同的值,因此必然会丢失一些信息——将 257 个项目打包到 256 个插槽中,必然会出现冲突。EOF
值在转换为 char
时,将变得与共享其数值的 256 个字符中的任何一个无法区分。如果该字符出现在文件中,上面的示例可能会将其误认为文件结束指示器;或者,同样糟糕的是,如果 char
类型是无符号的,那么由于 EOF
是负数,它永远不可能等于任何无符号 char
,因此上面的示例在文件结束时不会终止。它将永远循环,重复打印将 EOF
转换为 char
的结果的字符。
然而,如果 char 定义是有符号的(C 使默认 char 类型实现依赖于系统),[1] 假设常用的 EOF
值为 -1,则不会出现这种循环失败模式。但是,根本问题仍然存在,如果 EOF
值定义在 char
类型范围之外,则在赋值给 char
时,该值会被切片,并且不再与退出循环所需的完整 EOF
值匹配。另一方面,如果 EOF
在 char
的范围内,这保证了 EOF
和 char 值之间存在冲突。因此,无论系统类型如何定义,在测试 EOF
时永远不要使用 char
类型。
在 int
和 char
大小相同的系统上(即,与至少满足 POSIX 和 C99 标准的系统不兼容的系统),即使是“好的”示例也会受到 EOF
和某个字符的值无法区分的影响。处理这种情况的正确方法是在 getchar
返回 EOF
后检查 feof
和 ferror
。如果 feof
指示文件结束尚未到达,并且 ferror
指示没有发生错误,那么 getchar
返回的 EOF
可以被认为代表一个实际的字符。这些额外的检查很少进行,因为大多数程序员假设他们的代码永远不需要在这些“大 char
”系统上运行。另一种方法是使用编译时断言来确保 UINT_MAX > UCHAR_MAX
,这至少可以防止带有这种假设的程序在这样的系统上编译。
在 C 编程语言中,fread
和 fwrite
函数分别提供输入和输出的文件操作。fread
和 fwrite
在 <stdio.h>
中声明。
fwrite 的定义如下
size_t fwrite (const void *array, size_t size, size_t count, FILE *stream);
fwrite
函数将数据块写入流。它将写入一个包含 count
个元素的数组到流中的当前位置。对于每个元素,它将写入 size
字节。流的位置指示器将根据成功写入的字节数向前移动。
该函数将返回成功写入的元素数量。如果写入成功完成,返回值将等于 count
。如果发生写入错误,返回值将小于 count
。
以下程序打开一个名为sample.txt的文件,将一串字符写入文件,然后关闭它。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
size_t count;
const char *str = "hello\n";
fp = fopen("sample.txt", "w");
if(fp == NULL) {
perror("failed to open sample.txt");
return EXIT_FAILURE;
}
count = fwrite(str, 1, strlen(str), fp);
printf("Wrote %zu bytes. fclose(fp) %s.\n", count, fclose(fp) == 0 ? "succeeded" : "failed");
return EXIT_SUCCESS;
}
fputc
函数用于将一个字符写入流。
int fputc(int c, FILE *fp);
参数 c
在输出之前会默默地转换为 unsigned char
。如果成功,fputc
将返回写入的字符。如果失败,fputc 将返回 EOF
。
标准宏 putc
也在 <stdio.h>
中定义,其行为与 fputc
几乎相同,只是作为宏,它可能会多次计算其参数。
标准函数 putchar
也在 <stdio.h>
中定义,它只接受第一个参数,等效于 putc(c, stdout)
,其中 c
是该参数。
以下 C 程序打开一个名为 myfile 的二进制文件,从中读取五个字节,然后关闭该文件。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
const int count = 5; /* count of bytes to read from file */
char buffer[count] = {0}; /* initialized to zeroes */
int i, rc;
FILE *fp = fopen("myfile", "rb");
if (fp == NULL) {
perror("Failed to open file \"myfile\"");
return EXIT_FAILURE;
}
for (i = 0; (rc = getc(fp)) != EOF && i < count; buffer[i++] = rc)
;
fclose(fp);
if (i == count) {
puts("The bytes read were...");
for (i = 0; i < count; i++)
printf("%x ", buffer[i]);
puts("");
} else
fputs("There was an error reading the file.\n", stderr);
return EXIT_SUCCESS;
}
- ↑ C99 §6.2.5/15