1. 简介
文件是数据的一种组织形式,以字节序列的形式存在于磁盘上。C语言提供了一个FILE对象,所有的文件操作都是围绕这个FILE对象来进行的,文件的基本信息就位于标准库文件<stdio.h>的FILE对象中。
开始对文件进行操作之前,首先定义一个FILE指针,FILE指针指向要操作的文件,用来保存文件信息。
FILE *f;//定义一个文件指针
2. 标准流
程序执行时会默认打开三个文件,可用于访问屏幕和键盘。
- stdin:标准输入,键盘,编号为0;
- stdout:标准输出,屏幕,编号为1;
- stderr:标准错误,屏幕,编号为2。
3. 打开和关闭文件
3.1 fopen()函数
fopen()函数用于打开一个文件,这是文件读写操作的第一步。函数存在于库文件<stdio.h>中,原型如下。
FILE *fopen(const char *filename, const char *mode);
fopen()函数接受两个参数,第一个参数是文件名称,第二个参数是访问模式,用于指定对文件的操作方式。该函数返回一个FILE指针,如果文件打开失败,则返回NULL(空指针)
FILE *f;
f = fopen("test.c", "r");//打开文件
上面的fopen()函数打开了一个test.c文件,模式为r,表示以读取模式打开文件。
fopen()函数的访问模式还有很多:
3.2 fclose()函数
fclose()函数用于关闭一个文件,这是文件读写操作的最后一步。函数存在于库文件<stdio.h>中,原型如下。
int fclose(FILE *f);
fclose()函数接受一个文件指针参数,用于指定要关闭的文件。该函数返回一个int类型的整数值,如果关闭成功,返回0;如果关闭失败,则返回-1,即EOF。
注意:EOF是一个宏,一般读到文件末尾,就会返回这个值。
3.3 freopen()函数
freopen()函数也是打开一个文件,它用于重定向到指定的文件中,函数原型如下。
FILE* freopen(char* filename, char* mode, FILE* f);
freopen()函数比fopen()函数多了一个参数:FILE指针f,表示重定向的文件指针。
freopen("test.txt", "w", stdout);
上面的示例中,将文件test.txt关联到stdout,向stdout写入的内容,都会写到test.txt文件中。
4. 文件的读写
4.1 fgetc()与getc()
fgetc()函数和getc()函数的功能是一样的,用于从任意指定的文件中读取一个字符,区别在于getc()函数用宏实现的,fgetc()函数是用函数实现的,且都位于标准库头文件<stdio.h>中,函数的原型如下。
int fgetc(FILE *stream);
int getc(FILE *stream);
这两个函数都接受一个FILE指针,表示指向该文件。两者的返回值类型都是int,如果读取成功,返回对应的字符;如果读取失败则返回EOF值,即-1。
#include <stdio.h>
int main(void){
FILE *f;//创建FILE指针
f = fopen("test.txt","r");
int c;
while((c = getc(f)) != EOF){
printf("%c\n",c);
}
fclose(f);
}
4.2 fputc()与putc()
fputc()函数和putc()函数的功能是一样的,用于向任意指定的文件中写入一个字符,区别在于fgetc()函数用宏实现的,getc()函数是用宏实现的,且都位于标准库头文件<stdio.h>中,函数的原型如下。
int fputc(int char, FILE *stream);
int putc(int char, FILE *stream);
这两个函数都接受两个参数,第一个参数是要写入的字符,第二个参数是FILE指针。两者返回值都是int类型,如果写入成功,则返回写入的字符;如果写入失败,则返回EOF值,即-1。
4.3 fprintf()与fscanf()
fprintf()函数用于向任意指定的文件中写入格式化字符串,定义在标准库头文件<stdio.h>中,函数原型如下。
int fprintf(FILE* stream, const char* format, ...)
该函数的第一个参数是FILE指针,第二个参数是指定格式,后面的参数是可变参数,和printf()函数类似。
fprintf(f, "%d\n", test);//向文件指针f写入指定格式的字符串
fprintf()函数可以实现printf()函数的功能,文件指定为stdout就可以了。
fprintf(stdout, "peiqiblog\n");
//等同于
printf("peiqiblog\n");
fscanf()函数用于从任意指定的文件中按照指定格式读取内容,定义在标准库头文件<stdio.h>中,函数原型如下。
int fscanf(FILE* stream, const char* format, ...);
该函数的第一个参数是FILE指针,第二个参数是指定格式,后面的参数是可变参数,和scanf()函数类似。返回值是int类型,如果读取成功,返回读取的变量数量,如果读取失败则返回EOF值,即-1。
fscanf(f, "%d", &i);
4.4 fgets()与fputs()
fgets()函数用于从任意指定的文件中读取指定长度的字符串,定义在标准库头文件<stdio.h>中,函数原型如下。
char* fgets(char* str, int STRLEN, File* f);
该函数接受三个参数,第一个参数是字符指针,用于存放读取的内容;第二个参数指定读取的字符串长度,第三个参数是FILE指针,指定要读取的文件。
fgets()函数读取到STRLEN-1字符后,或者读到了文件结尾或者遇到了换行符\n,就会停止读取,然后在已读取的内容末尾添加空字符\0表示结束。
fgets()函数返回值是char类型,指向该函数的第一个参数,否则读取失败,就会返回NULL。
#include <stdio.h>
int main(void){
char c[256];
FILE *f;
f = fopen("test.txt","r");
while (fgets(c, sizeof c, fp) != NULL){ //循环读取文件的每一行
printf("%s",c);
}
fclose(f);
}
上面的示例使用fgets()函数读取文件的每一行,保存到字符指针c中并输出到屏幕上。
fputs()函数用于向任意指定的文件中写入字符串,定义在标准库头文件<stdio.h>中,函数原型如下。
int fputs(const char* str, FILE* stream);
该函数接受两个参数,第一个参数是要写入的字符串指针,第二个参数是FILE指针,指向要写入的文件。fputs()函数通常与fgets()函数配合使用。该函数返回一个int整数,写入失败则返回EOF值。
char *str = "peiqiblog";
fputs(str,stdout);
4.5 fread()与fwrite()
fread()函数是比较常用的,它用于从任意指定的文件中一次性读取较大的数据块,然后写入到数组中,常用于读取二进制数据。它定义在标准库文件<stdio.h>中,函数原型如下。
size_t fread(void *ptr,size_t size,size_t nmemb,FILE *stream)
该函数的功能是从指定的文件流stream读取数据存放到ptr指针指向的数组中。该函数有四个参数:
- void *ptr:指向数组的指针,即数组的地址;
- size_t size:读取的每个数组元素的大小,以字节为单位;
- size_t nmemb:数组元素的个数,每个元素的大小为size个字节;
- FILE *stream:指向文件的指针,这里指定了输入流。
该函数的返回值类型为size_t,如果读取成功,返回所读取的元素数量,也就是该函数的第三个参数nmemb,一般是与nmemb是相同的;如果读取失败,则返回值比nmemb小。
FILE *stream;
char str[];
stream = fopen("test.c","r");
while (fread(&str, sizeof(char), 1, stream) > 0){
printf("%d\n", str);
}
fclose(stream);
上面的示例是调用fread()函数循环读取test.c文件中。
fwrite()函数和fread()函数表现基本类似,它用于向任意指定的文件中一次性写入较大的指定的数据块,常用于写入二进制数据。它定义在标准库文件<stdio.h>中,函数原型如下。
size_t fwrite(const void *ptr,size_t size,size_t nmemb,FILE *stream)
该函数的功能是将指针ptr指向的数组中的数据写入到指定的文件流stream中。该函数有四个参数:
- void *ptr:指向数组的指针,这里指要被写入的数组的地址;
- size_t size:被写入的每个数组元素的大小,以字节为单位;
- size_t nmemb:数组元素的个数,每个元素的大小为size个字节;
- FILE *stream:指向文件的指针,这里指定了输出流。
该函数的返回值类型为size_t,如果写入成功,返回所写入的元素总数量,也就是该函数的第三个参数nmemb,一般是与nmemb是相同的;如果写入失败,则返回值比nmemb小。
FILE *stream;
stream = fopen("test.c","w");//打开文件
char str[] = "peiqiblog";
fwrite(str,sizeof(str), 1,stream);//写入字符串
fclose(stream);
上面的示例是将字符串str的内容写入到test.c文件中。
4.6 fseek()
fseek()函数用于将任意指定的文件的定位设置为指定的位置,定义在标准库头文件<stdio.h>中,函数原型如下。
int fseek(FILE *stream, long int offset, int whence)
fseek()函数用于将指定的文件流stream的位置为指定的偏移offset,它接受三个参数:
- FILE *stream:指向FILE文件的指针;
- long int offset:相对 whence 的偏移量,以字节为单位;
- int whence:表示开始添加偏移 offset 的位置,即offset的参照物。
whence参数一般指定为如下常量:
- SEEK_SET:文件开始处;
- SEEK_CUR:文件指针的当前位置;
- SEEK_END:文件结尾处。
如果fseek()函数执行成功,则返回0,;如果操作失败,则返回一个负值。
FILE *stream;
stream = fopen("test.c","rw");
//定位到文件的开始处
fseek(stream,0L,SEEK_SET);
//定位到文件的第5个字节
fseek(stream,5L,SEEK_SET);
//定位到文件的当前位置后5个字节
fseek(stream,5L,SEEK_CUR);
//定位到文件的结尾
fseek(stream,0L,SEEK_END);
//定位到文件的结尾处前5个字节
fseek(stream,-5L,SEEK_END);
fclose(stream);
上面的示例中分别运用了三个基准常量值,可以定位到文件的各个位置,注意这里的偏移量是long类型,所以使用时要加上后缀L,转为long类型。
4.7 ftell()
ftell()函数用于获取指定文件内的当前位置,定义在标准库文件<stdio.h>中,函数原型如下。
long int ftell(FILE* stream);
该函数接受一个FILE指针作为参数,指向要操作的文件。返回值类型是long int。如果执行成功,则返回文件的开始到当前位置的字节数;如果执行失败,则返回-1L。ftell()函数通常与fseek()函数一起使用。
FILE *stream;
stream = fopen("test.c","rw");
fseek(stream,0L,SEEK_CUR);
long int size = ftell(stream);
fclose(stream);
上面的示例中,先使用fseek()函数定位到文件的当前位置,然后再获取从文件开始处到当前位置的字节数。
4.8 feof()
feof()函数用于判断文件内部(内部指针)是否到达了结尾,定义在标准库文件<stdio.h>中,函数原型如下。
int feof(FILE *fp);
该函数接受一个FILE指针为参数,表示要操作的文件。该函数返回一个int类型的值,如果内部指针已经指向了文件结尾,那么返回一个为零值;否则就返回0。
FILE *stream;
stream = fopen("test.c","rw");
int c;
while(1){
c = fgetc(stream);
if(foef(stream)){
break;
}
printf("%c\n",c);
}
fclose(stream);
上面是一个读取文件输出字符的示例,通过使用foef()函数判断是否到达结尾,到达结尾则退出循环。
4.9 ferror()
ferror()函数用于记录文件的错误状态,判断文件操作是否成功,定义在标准库文件<stdio.h>中,函数原型如下。
int ferror(FILE *stream);
该函数接受一个FILE指针,用于指向该文件。返回值是一个int类型,如果前面的文件操作成功,则返回0;如果操作中有错误,则返回一个非零值。
FILE *stream;
stream = fopen("test.c","rw");
int c;
while(1){
c = fgetc(stream);
if (ferror(stream)) {
printf("读取文件test.txt中字符时发生错误\n");
}
}
fclose(stream);