[C语言]文件操作函数
本文主要学习**fopen
,fclose
,fgetc
,fgets
,fputc
,fputs
,fwrite
,fread
,feof
**这几个文件操作函数。
以上函数,需要导入头文件 stdio.h
为什么要学习这些函数?肯定是因为我们需要存储我们的数据,使数据放入到硬盘中方便保存。
本文中梳理一些最常见的文件操作函数。
前言:文本文件和二进制文件
根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件
一.文件的打开与关闭
文件指针
缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE.
文件指针的创建
FILE* pf
定义pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件。
二. fopen
和 fclose
fopen
和fclose
一般是成对出现的,有 fopen
就会有 fclose
fopen
是打开文件的函数,fclose
是关闭文件的函数
函数参数介绍
//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
fclose ( FILE * stream);
fopen
的第一个参数,表示要打开的文件,如果在本目录下,直接书写文件名即可,如果不在本目录,就需要写出文件绝对路径。
fopen
的第二个参数表示对文件的操作形式,可看下图来学习。
fclose
则是关闭我们打开的文件。
实例
在使用完fopen
函数后,最好判断一下, fopen
文件函数是否返回空指针NULL
。
int main()
{
FILE* pr = fopen("data.txt", "r");//第一个参数是我要打开的文件名,第二个参数代表对文件的操作是“只读”
if (NULL == pr)//判断pr是否文件是否打开成功
{
printf("open for reading : %s", strerror(errno));
return 0;
}
FILE* pw = fopen("data2.txt", "w");//第一个参数是我要打开的文件名,第二个参数代表对文件的操作是“写入”
if (NULL == pw)//判断文件是否打开成功
{
printf("open for writing : %s", strerror(errno));
fclose(pr);//如果打开此文件失败,则关闭上一个文件。
pr = NULL;
return 0;
}
fclose(pr);//文件使用完成后,关闭文件
pr = NULL;//并将文件指针置空
fclose(pw);
pw = NULL;
return 0;
}
三. fputc
和 fgetc
这两个文件函数在上个案列的代码演示中已经出现, fgetc
是读取文件中的字符, fputc
是将字符输出到文件中。
//fgetc的函数参数
int fgetc( FILE *stream );//stream代表将要读取字符的文件
//fputc的函数参数
int fputc( int c, FILE *stream );// c 代表我们将要存入的字符,stream是我们存放 c 的文件。
实例
这是结合了上一个实例而成的。
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pr = fopen("data.txt", "r");//第一个参数是我要打开的文件名,第二个参数代表对文件的操作是“只读”
if (NULL == pr)//判断pr是否文件是否打开成功
{
printf("open for reading : %s", strerror(errno));
return 0;
}
FILE* pw = fopen("data2.txt", "w");//第一个参数是我要打开的文件名,第二个参数代表对文件的操作是“写入”
if (NULL == pw)//判断文件是否打开成功
{
printf("open for writing : %s", strerror(errno));
fclose(pr);//如果打开此文件失败,则关闭上一个文件。
pr = NULL;
return 0;
}
int ch = 0;
while ((ch = fgetc(pr)) != EOF)//将“data.txt”文件的所有所有内容都写进“data2.txt”这个文件
{
fputc(ch, pw);
}
return 0;
}
四. fgets
和 fputs
fgets
是读取文件中数据的一行,相反,fputs
则是输出一行到文件。
函数参数介绍
//fgets的函数参数
char *fgets( char *string, int n, FILE *stream );//第一个参数是我们接收文件中数据的字符串,第二个参数 n 表示我们要读取几个字符,不过字符数是 (n-1)个字符,第三个参数则是我们要读取的文件。
//fputs的函数参数
int fputs( const char *string, FILE *stream );//这个函数的参数实际和fgetc差不多,不过这里输出的是字符串到文件stream中,而不是一个字符。
实例
将数据输入到文件中,再次提醒,如果文件不在本目录下,需要加上绝对路径
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("data.txt", "w");
if (NULL == pf)
{
printf("open for write : %s", strerror(errno));
return 0;
}
fputs("hello world\n", pf);
fputs("haha\n", pf);
fclose(pf);
pf = NULL;
return 0;
}
读取文件
int main()
{
FILE* pf = fopen("data.txt", "r");
if (NULL == pf)
{
printf("open for write : %s", strerror(errno));
return 0;
}
char arr[2] = { 0 };//开辟两个空间来接收数据
fgets(arr, 13, pf);// fgets只能读取(n-1)个字符,所以是13
printf("%s", arr);
fgets(arr+1, 5 , pf);
printf("%s", arr+1);
fclose(pf);
pf = NULL;
return 0;
}
五.fwrite
和 fread
fwrite
是二进制输出,也就是说,将内容以二进制的形式进行输出到文件中保存。
fread
则是读取文件的以二进制保存的数据。
函数参数介绍
//fwrite函数参数
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );//第一个参数buffer表示我们要输出的数据的地址,第二参数size是我们要输出的数据大小,第三个参数是count是我们要输入的数据个数,第四个参数则是我们要将数据输入到的文件。
//fread函数参数
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );//第一个参数buffer表示我们要接收的数据的地址,第二参数size是我们要接收的数据大小,第三个参数是count是我们要接收的数据个数,第四个参数则是我们要将数据输入到的buffer的文件。
实例
fwrite
二进制输出
#include <stdio.h>
#include <errno.h>
#include <string.h>
struct Stu
{
int age;
char name[10];
char id[20];
};
int main()
{
struct Stu s[2] = { { 12, "zhangsan", "253534123" }, {13,"lisi","342342354"}};
FILE* pr = fopen("date.txt", "wb");//wb表示:为了输出数据,打开一个二进制文件
if (NULL == pr)
{
printf("open for writing : %s", strerror(errno));
}
fwrite(&s, sizeof(struct Stu), 2, pr);
fclose(pr);
pr = NULL;
return 0;
}
运行后:
使用 fread
读取date.txt
中的数据
#include <stdio.h>
#include <errno.h>
#include <string.h>
struct Stu
{
int age;
char name[10];
char id[20];
};
int main()
{
struct Stu s[2] = { 0 };//开辟两个相同的结构体数组来接收数据
FILE* pr = fopen("date.txt", "rb");
if (NULL == pr)
{
printf("open for writing : %s", strerror(errno));
}
fread(s, sizeof(struct Stu), 2, pr);
printf("%s %d %s\n", s[0].name, s[0].age, s[0].id);
printf("%s %d %s\n", s[1].name, s[1].age, s[1].id);
fclose(pr);
pr = NULL;
return 0;
}
文件读取成功
六.feof
应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。
1.文本文件读取是否结束,判断返回值是否为 EOF ( fgetc )
,或者 NULL ( fgets )
例如:
fgetc
判断是否为 EOF
.
fgets
判断返回值是否为 NULL
.
2.二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如:
fread
判断返回值是否小于实际要读的个数。
实例
文本文件的例子
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int c; // 注意:int,非char,要求处理EOF
FILE* fp = fopen("test.txt", "r");
if(!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
二进制文件的例子
#include <stdio.h>
enum { SIZE = 5 };
int main(void) {
double a[SIZE] = {1.,2.,3.,4.,5.};
FILE *fp = fopen("test.bin", "wb"); // 必须用二进制模式
fwrite(a, sizeof *a, SIZE, fp); // 写 double 的数组
fclose(fp);
double b[SIZE];
fp = fopen("test.bin","rb");
size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 读 double 的数组
if(ret_code == SIZE) {
puts("Array read successfully, contents: ");
for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]);
putchar('\n');
} else { // error handling
if (feof(fp))
printf("Error reading test.bin: unexpected end of file\n");
else if (ferror(fp)) {
perror("Error reading test.bin");
}
}
fclose(fp);
}