文件的输出入定义在 stdio.h 标头文件,若要开启文件,可以使用fopen
,其函数原型定义如下:
FILE* fopen( const char* filename, const char* mode )
FILE
是个结构类型:
typedef struct _iobuf {
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
} FILE;
fopen
返回FILE
实例的地址,若将FILE
的地址传给fgetc
、fputc
、fgets
、fputs
等函数,字符串流只是在读取或写入的过程,会进行文本编码的转换,例如int
数字 9,在写入的操作中,会转换为编码 57 的字节数据,至于本身是char
的数据,就直接以对应的字节写出,也就是所谓的纯文本文件读写。
fopen
第一个参数用来指定文件名称,第二个参数用来指定输出入模式,模式基本上就是读、写、扩充等,分别可使用r
、w
与a
等设定,若加上+
, 表示扩充文件读写能力。例如,以下是可设定的模式:
- r:只读模式开启文件,若文件不存在,返回 NULL。
- w:唯写模式创建文件,若文件不存在,创建新文件,若文件存在,就删除其内容。
- a:附加模式开启文件,若文件不存在,创建新文件,若文件存在,从文件尾端写入。
- r+:开启文件进行读写,若文件不存在,返回 NULL,若文件存在,从文件开头进行读写。
- w+:创建文件进行读写,若文件不存在,创建新文件,若文件存在,就删除其内容。
- a+:开启文件进行读写,若文件不存在,创建新文件,若文件存在,从文件尾端写入。
可以选择性地加上b
表示以二进制模式,在 POSIX 系统上会忽略,Windows 会不处理\n
与\x1A
。
例如,以下可开启一个文件进行读取:
FILE *fp = fopen("test.txt", "r");
可以使用以下片段来测试文件是否开启成功:
if(!fp) {
perror("文件开启失败"); // 将消息输出至 stderr
return EXIT_FAILURE;
}
开启文件之后,可以使用fgetc
来读取文件中的字符,使用fputc
来将字符写入文件,以读取为例:
int c;
while ((c = fgetc(fp)) != EOF) {
putchar(c);
}
ferror
可以检查文件读写是否有误,feof
可检查读取到文件尾:
if (ferror(fp)) {
puts("读取时发生错误");
}
else if (feof(fp)) {
puts("读取成功");
}
fopen
会使用缓冲区减少对磁碟的读写,不使用文件时,记得关闭文件,关闭文件会将缓冲区中的数据写入磁碟,若忘了关闭文件,可能会造成数据遗失,可以使用fclose
来关闭文件:
fclose(fp);
下面这个程序示范如何读取来源文件并将内容写为另一文件:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
FILE* src = fopen(argv[1], "r");
if(!src) {
perror("无法开启来源文件");
return EXIT_FAILURE;
}
FILE* dest = fopen(argv[2], "w");
if(!dest) {
perror("无法创建目标文件");
return EXIT_FAILURE;
}
int c;
while ((c = fgetc(src)) != EOF) {
fputc(c, dest);
}
if (ferror(src) || ferror(dest)) {
puts("读写时发生错误");
}
fclose(src);
fclose(dest);
}
也可以使用fgets
来读取整个字符串,使用fputs
来写入整个字符串:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
FILE* src = fopen(argv[1], "r");
if(!src) {
perror("无法开启来源文件");
return EXIT_FAILURE;
}
FILE* dest = fopen(argv[2], "w");
if(!dest) {
perror("无法创建目标文件");
return EXIT_FAILURE;
}
char buf[8];
while (fgets(buf, sizeof(buf), src) != NULL) {
fputs(buf, dest);
}
if (ferror(src) || ferror(dest)) {
perror("读写时发生错误");
}
fclose(src);
fclose(dest);
}
fgets
第一个参数为用来存储读入的数据,第二个参数为读入的字符长度,第三个参数为FILE
地址值,而fputs
第一个参数为写入的数据,第二个参数为FILE
地址值。
以下的程序使用fgets
、fputs
改写以上范例:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]) {
FILE* src = fopen(argv[1], "r");
if(!src) {
perror("无法开启来源文件");
return EXIT_FAILURE;
}
FILE* dest = fopen(argv[2], "w");
if(!dest) {
perror("无法创建目标文件");
return EXIT_FAILURE;
}
char buf[8];
while (fgets(buf, sizeof(buf), src) != NULL) {
fputs(buf, dest);
}
if (ferror(src) || ferror(dest)) {
perror("读写时发生错误");
}
fclose(src);
fclose(dest);
}
在程序执行过程开启的标准输出stdout
、标准输入stdin
、标准错误stderr
,事实上也是文件的特例,在 C 程序中,也常见到以下的方式,以便直接控制这三个标准输入、输出、错误:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]) {
FILE* src = fopen(argv[1], "r");
if(!src) {
fputs("无法开启来源文件", stderr);
return EXIT_FAILURE;
}
FILE* dest = fopen(argv[2], "w");
if(!dest) {
fputs("无法创建目标文件", stderr);
return EXIT_FAILURE;
}
char buf[8];
while (fgets(buf, sizeof(buf), src) != NULL) {
fputs(buf, dest);
}
if (ferror(src) || ferror(dest)) {
fputs("读写时发生错误", stderr);
}
fclose(src);
fclose(dest);
}