C语言文件操作(超级详细)

一、文件的打开和关闭

1. fopen()

        第二个参数为文件的打开方式:mode:传入文件的打开方式(打开方式有:"r","w","a","rb","wb","ab","r+","w+","a+","rb+","wb+","ab+"):

注:当使用“w”,“wb”,“w+”,“wb+”打开文件时会清除文件原本存储的数据。

        该函数的返回值是一个FILE类型的指针,在文件成功打开后返回此文件信息区的起始地址,打开失败则返回一个NULL(空指针),所以使用fopen时需要一个FILE类型的指针来接收其返回值。

	//打开
	FILE* pf = fopen("text.txt","w");
	//判断是否打开成功
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

2.fclose()

 stream:传入将要关闭的文件的文件指针。

        该函数的返回值是int类型的:如果关闭成功就返回0值,否则返回EOF(-1)值。

int main()
{
	//打开
	FILE* pf = fopen("text.txt","w");
	//判断是否打开成功
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//关闭
	if (fclose(pf) == EOF)
	{
		//关闭失败
		perror("fclose");
		return 1;
	}
	pf = NULL;
	return 0;
}

 在关闭文件成功之后我们应该将原本的文件指针置为空(NULL),避免野指针的产生。 

二、文件的读写

1.顺序读写

2.1.1fputc()

该函数的作用是用来向文件输入单个字符数据的

            该函数有int类型的character和FILE*类型的stream两个参数:

                character:接收所要存入的字符的ASCII值

                stream:接收指向所要存入文件的指针

int main()
{
	//打开
	FILE* pf = fopen("text.txt", "w");
	//判断是否打开成功
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写入数据
	int i = 'a';
	for (i = 0; i < 26; i++)
	{
        fputc('a' + i, pf);
	}
	//关闭
	if (fclose(pf) == EOF)
	{
		//关闭失败
		perror("fclose");
		return 1;
	}
	pf = NULL;
	return 0;
}

2.1.2fgetc()

该函数一般被用来读取文件中单个字符数据

 该函数有一个FILE*类型的参数stream:

                stream:接收指向所要读取文件的指针

        该函数成功读入数据时会返回读取字符的ASCII值,反之则会返回EOF(-1)值。

int main()
{
	//打开
	FILE* pf = fopen("text.txt", "r");//此时读取文件要用"r"的方式打开
	//判断是否打开成功
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读入数据
	int i;
	while ((i = fgetc(pf)) != EOF)
	{
		printf("%c ", i);
	}
	//关闭
	if (fclose(pf) == EOF)
	{
		//关闭失败
		perror("fclose");
		return 1;
	}
	pf = NULL;
	return 0;
}

2.1.3 fputs()

读写文件一个一个字符操作太麻烦了,我们可以使用fputs函数直接向文件写入一个字符串

                str:接收指向将要存入字符串的指针

                stream:接收指向所要存入文件的指针

        该函数成功运行返回一个非负值,否则返回EOF(-1)值。

int main()
{
	//打开
	FILE* pf = fopen("text.txt", "w");
	//判断是否打开成功
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//存入数据
	fputs("hello", pf);
	fputs("world", pf);
	//关闭
	if (fclose(pf) == EOF)
	{
		//关闭失败
		perror("fclose");
		return 1;
	}
	pf = NULL;
	return 0;
}

 2.1.4fgets()

对于输出文件文本行(字符串)数据一般使用fgets函数来进行操作

         str:接收指向储存读出数据的字符串指针

        num:接收要复制到 str 中的最大字符数(包括终止空字符)

        stream:接收指向所要读取文件的指针

成功后,该函数返回 str的头指针
如果在尝试读取字符时遇到文件结尾,则会设置 EOF 指示符 。如果在读取任何字符之前发生这种情况,则返回的指针为空指针(并且 str 的内容保持不变)。
如果发生读取错误,则设置错误指示器 (ferror) 并返回空指针(但 str 所指向的内容可能已更改)。

2.1.5fprintf()

可以向文件输出任意数据

仔细观察发现fprintf比printf函数唯一多出来的一个参数就是FILE*类型的stream。

         如此一来就很好解释fprintf函数的用法了:

        stream:接收指向所要输出文件的指针

        format:接收将要输出数据的格式(和printf函数一样有%d,%x,%c,%s等等格式类型)

        至于后面的...是接收所要输出的数据(如printf函数在格式后输入所要打印的变量一样)

struct S
{
	int age;
	char name[20];
	char sex[5];
};
int main()
{
	struct S L = { 20,"张三","男" };
	//打开
	FILE* pf = fopen("text.txt", "w");
	//判断是否打开成功
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//输入数据
	fprintf(pf, "%d %s %s", L.age, L.name, L.sex);
	//关闭
	if (fclose(pf) == EOF)
	{
		//关闭失败
		perror("fclose");
		return 1;
	}
	pf = NULL;
	return 0;
}

2.1.6fscanf()

可以读取各种类型的数据的函数

仔细观察发现fscanf比scanf函数唯一多出来的一个参数也是FILE*类型的stream。

                那和scanf函数的用法也大同小异了:

        stream:接收指向所要输入文件的指针

        format:接收将要输入数据的格式(和scanf函数一样有%d,%x,%c,%s等等格式类型)

        至于后面的...是接收所要输入的数据的变量(如scanf函数在格式后输入所要改变的变量一样)

运行成功后,该函数返回所读取数据的个数(甚至为零),如果在读取时发生读取错误或到达文件末尾,则会设置正确的指示器(feof 或 ferror)。而且,如果在成功读取任何数据之前发生任何一种情况,则返回 EOF。

注:使用fscanf和scanf函数时一样非字符串类型的所要储存输入数据的变量需要&

struct S
{
	int age;
	char name[20];
	char sex[5];
};
int main()
{
	struct S s;
	//打开
	FILE* pf = fopen("text.txt", "r");
	//判断是否打开成功
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//输出数据
	fscanf(pf, "%d %s %s", &(s.age), s.name, s.sex);
	printf("%d %s %s", s.age, s.name, s.sex);
	//关闭
	if (fclose(pf) == EOF)
	{
		//关闭失败
		perror("fclose");
		return 1;
	}
	pf = NULL;
	return 0;
}

 2.1.7fwrite()

        我们在向文件输出数据时都是以文本格式输出的,但是fwrite函数可以直接将计算机内存中所存储的二进制数据输出到文件中(此时此文件就是一个二进制文件)

        fwrite函数可以直接将计算机内存中所存储的二进制数据输出到文件中(此时此文件就是一个二进制文件)

        ptr:接收指向所要输入数据的指针或地址

        size:接收输出的每个数据的大小(以字节为单位)。

        count:所要输出数据的个数

        stream:接收指向所要输出文件的指针

成功运行返回输出的数据总数,如果此数字与 count 参数不同,则写入错误会阻止函数完成。在这种情况下,将为流设置误差指示器(ferror),如果大小或计数为零,则该函数返回零,并且错误指示器保持不变。

struct S
{
	int age;
	char name[20];
	char sex[5];
};
int main()
{
	struct S L = { 20,"李四","女" };
	//打开
	FILE* pf = fopen("text.txt", "wb");//二进制输出时用wb
	//判断是否打开成功
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//输出数据
	fwrite(&L, sizeof(struct S), 1, pf);
	//关闭
	if (fclose(pf) == EOF)
	{
		//关闭失败
		perror("fclose");
		return 1;
	}
	pf = NULL;
	return 0;
}

2.1.8fread()

对于二进制的文件数据我们就要使用二进制的方式读取

        ptr:传入指向将要存储数据的变量的指针或地址

        size:传入每次从文件读取数据的大小(以字节为单位)

        count:传入将要读取的次数

        stream:接收指向所要输入文件的指针

该函数运行成功后返回读取数据的次数,否则返回0值。

struct S
{
	int age;
	char name[20];
	char sex[5];
};
int main()
{
	struct S s;
	//打开
	FILE* pf = fopen("text.txt", "rb");//二进制输出时用rb
	//判断是否打开成功
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//输入数据
	if (fread(&s, sizeof(struct S), 1, pf) != 0)
	{
		printf("%d %s %s", s.age, s.name, s.sex);
	}
	//关闭
	if (fclose(pf) == EOF)
	{
		//关闭失败
		perror("fclose");
		return 1;
	}
	pf = NULL;
	return 0;
}

 2.1.9标准输入流,标准输出流,标准错误流

                stdin——标准输入流——键盘

                stdout——标准输出流——屏幕

                stderr ——标准错误流——屏幕

        系统自动打开三个FILE*类型的流:stdin(接收键盘数据),stdout(接收所要向屏幕输出的数据),stderr(接收所要向屏幕输出的数据)。

2.随机读写

        随机读写并不是真的随机读写,而是通过当前的文件指针所指向的位置相对于文件的指针的起始位置的偏移量来读取任意想要读取的位置。

2.1文件指针的偏移量

2.2fseek()

该函数可以设置文件指针指向的位置及偏移量。

         stream:传入将要改变的文件指针(文件流)

        offsrt:传入指针所要偏移的偏移量

        origin:设置传入的stream指针的起始位置(可传入3种参数:SEEK_SET(设置指针指向文件开头)、SEEK_CUR(不改变指针位置)、SEEK_END(设置指针指向文件结束位置))

        该函数运行成功,返回零。否则回非零值。

        如果发生读取或写入错误,则设置错误指示器(ferror)。

2.3ftell()

  ftell函数可以返回文件指针相对于起始位置的偏移量:

stream:传入所需计算偏移量的文件指针

成功后,返回位置指示器的当前值。失败时,返回 -1,并将 errno 设置为系统特定的正值。

2.4rewind()

 该函数可以将所传入的文件指针设置指向文件初始位置:

         stream:传入将要改变的文件指针

三、文本文件和二进制文件

 根据数据的组织形式,数据文件被分为文本文件或者二进制文件。

                数据在内存中以二进制的形式存储,如果不加转换的输出到外存(磁盘文件等等),就是二进制文件。

                如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。

                数据在内存中字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。

                我们拿10000这个数据来举例,用ASCll码来存储需要5个字节(因为10000是五位数,每一位数都要用一个字节的ASCll码来表示),而用二进制存储只需要四个字节(一个整型大小为4字节)。

四、文件读取结束的判定

4.1feof()

 在我们使用读取文件的函数时,都讲解了其返回值,我们可以通过其返回值来判定对文件的读取是否成功(这里不再一一举例)。

        补充一个用来判断文件为什么结束读取的函数:

        feof函数 
                对于文件读取结束之后,我们可以使用feof函数来文件是为何而读取结束的

     该函数只有一个FILE*类型的参数:

        stream:传入所需要判断的文件指针

当传入的文件流是遇到文件末尾而结束读取时该函数返回非0值,其他原因返回0。

五、文件缓冲区

        ANSIC标准采用"缓冲文件系统"处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块"文件缓冲区"。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。

        以下是程序向硬盘输入数据和硬盘向程序输出数据的流程:

int main()
{
	//打开
	FILE* pf = fopen("text.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//存入
	int a = 10000;
	fwrite(&a, sizeof(int), 1, pf);
	printf("此20秒数据在文件缓冲区内,打开文件是没有数据的\n");
	Sleep(20000);//睡眠10秒,睡眠时打开文件可以观察
	fflush(pf);//此函数可以刷新缓冲区中的数据,使其存入硬盘文件中
	printf("此20秒数据从文件缓冲区内读入到文件中,打开文件是有数据的\n");
	Sleep(20000);
	//关闭
	fclose(pf);//fclose()也会清理缓冲区,因此前面也要加Sleep()
	pf = NULL;
	return 0;
}

  • 32
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值