C语言文件操作

一、文件的打开与关闭

  • FILE * fopen ( const char * filename, const char * mode ); —> 打开文件,第二个参数mode就是读写模式
  • 使用完成后,还需要关闭文件:int fclose ( FILE* stream);
#include <stdio.h>
int main ()
{
	  FILE * pFile;
	  //打开文件
	  pFile = fopen ("myfile.txt","w");
	  if (pFile==NULL)
	  {
	 		// 打开文件失败,无法进行操作
	 		printf("pFile is fail\n");
	 		return 0;
	  }
	  // pFile成功指向一块有效的文件信息区,可以进行文件操作了 
	  fputs ("fopen example",pFile);
	  //关闭文件
      fclose (pFile);
	  return 0;
}

常用的读写模式mode

模式含义如果指定文件不存在
“r”(只读)为了输入数据,打开一个已经存在的文本文件error
“w”(只写)为了输出数据,打开一个文本文件自动创建文件
“a”(追加)向文本文件尾追加数据自动创建文件
“rb”(只读)为了输入数据,打开一个二进制文件error
“wb”(只写)为了输入数据,打开一个二进制文件自动创建文件
“ab”(追加)向一个二进制文件尾添加数据error

二、文件的顺序读写

所有流:处理文件流外,电脑在每次开机运行的时候也会默认打开三个流,称为标准流。

  • stdout - 标准输出流
  • stdin - 标准输入流
  • stderror - 标准错误流

fgetc与fputc

  • fgetc - 字符输入函数,针对于所有流 int fgetc( FILE *stream );
  • fputc - 字符输出函数,针对于所有流 int fputc( int c, FILE *stream );
代码展示
// fgetc与fputc
int main()
{
	// 打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		// 打开失败
		printf("%s\n", strerror(errno));
		return 0;
	}

	// 使用文件
	/*int ch = 'a';
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		fputc(ch, pf);
	}*/
	for (int i = 0; i < 26; i++)
	{
		int ch = fgetc(pf);
		printf("%c", ch);
	}
	printf("\n");

	// 关闭文件
	fclose(pf);

	return 0;
}

fputc 将26个字母写入文件
在这里插入图片描述
fgetc 在文件中按字符一个个读取
在这里插入图片描述

fgets与fputs

注意:我学习的时候一直以为 fputs 是将一句话写在一行,而fgets则是读一行;其实fputs是直接写入,而fgets则是按需索取,需要传入读取个数

  • fgets - 文本行输入函数,针对所有流 char *fgets( char *string, int n, FILE *stream ); - 返回的是string的地址(从一个stream流里面读入n个字符到string中)
  • fputs - 文本行输出函数,针对所有流 int fputs( const char *string, FILE *stream );(将一个字符串写入到stream流里面)
代码展示
// fgets与fputs
int main()
{
	// 打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		// 打开失败
		printf("%s\n", strerror(errno));
		return 0;
	}

	// 使用文件
	/*fputs("hello world\n", pf);
	fputs("I Love You\n", pf);*/
	char arr[100] = { 0 };
	fgets(arr,10,pf);
	printf("%s\n", arr);

	// 关闭文件
	fclose(pf);

	return 0;
}

fputs - 写入两句话
在这里插入图片描述
fgets - 读数据
在这里插入图片描述

fscanf与fprintf

进行格式化的输入与输出的

  • fscanf - 格式化输入函数,针对所有流 int fscanf( FILE *stream, const char *format [, argument ]... );
  • fprintf - 格式化输出函数,针对所有流 int fprintf( FILE *stream, const char *format [, argument ]...);
代码演示
// fprintf与fscanf
struct Stu
{
	char name[20];
	int age;
};

int main()
{
	struct Stu s = { "chen", 20 };
	struct Stu tmp = { 0 };
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}

	//fprintf(pf,"%s %d\n", s.name, s.age);
	fscanf(pf, "%s %d", tmp.name, &(tmp.age));
	printf("%s %d\n", tmp.name, tmp.age);
	fclose(pf);

	return 0;
}

fprintf - 将一个结构体数据按照格式输出到文件里面

在这里插入图片描述

fscanf - 将文件里面的数据以格式化的形式输入到结构体

在这里插入图片描述

一组函数对比
printf(将数据打印到屏幕上)
  • int printf( const char *format [, argument]... ); - 向stdout格式化输出的函数
scanf(从键盘获取数据)
  • int scanf( const char *format [,argument]... ); - 从stdin格式化输入的函数
fprintf(将数据打印到文件中,stream = stdout时,相当于printf)

在这里插入图片描述

fscanf(从文件中获取数据,stream = stdin时,相当于scanf)

在这里插入图片描述

sprintf(将数据转化成字符串)

在这里插入图片描述

sscanf(将字符串中内容按指定格式进行转换)

在这里插入图片描述

sscanf与sprintf代码演示
struct Book
{
	char name[20];
	double price;
};

int main()
{
	struct Book book = { "C程序设计", 66.6 };
	struct Book tmp = { 0 };
	char arr[100] = { 0 };

	 sprintf(arr, "%s %lf", book.name, book.price);
	 printf("%s\n", arr);

	sscanf(arr, "%s %lf", tmp.name, &(tmp.price));
	printf("%s %lf\n", tmp.name, tmp.price);

	return 0;
}

在这里插入图片描述

fread与fwrite

  • fread - 二进制输入,只针对文件 size_t fread( void *buffer, size_t size, size_t count, FILE *stream ); - 将stream文件流中的内容输入到buffer地址处的变量中,每次输入count个size字节数的内容 ;返回值:实际写入的内容个数(也是用个数来判断是否结束的)
  • fwrite - 二进制输出,只针对文件 size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream ); - 将buffer地址处的内容输入到stream文件流中,每次输入count个size字节数的内容 ;返回值:实际输出到文件的内容个数(也是用个数来判断是否结束的)
代码展示
// fread与fwrite
#include <stdio.h>
int main()
{
	int a = 10000;
	int c = 0;
	FILE* pf = fopen("test.txt", "rb");
	if (pf == NULL)
		return 0;
	// fwrite(&a, 4, 1, pf);//二进制的形式写到文件中
	fread(&c, 4, 1, pf);
	printf("%d\n", c);

	fclose(pf);
	pf = NULL;
	return 0;
}

fwrite
在这里插入图片描述
fread
在这里插入图片描述

三、文件的随机读写

fseek

根据文件指针的位置偏移量来定位文件指针

  • int fseek ( FILE * stream, long int offset, int origin );
  • offset - 偏移量(正数向后,负数向前)
  • origin - 文件偏移的起始位置
    • SEEK_CUR - 当前指针位置
    • SEEK_END - 文件末尾
    • SEEK_SET - 文件开头

在这里插入图片描述

ftell

返回文件指针相对于起始位置的偏移量

  • long int ftell ( FILE * stream );

在这里插入图片描述

rewind

让文件指针的位置回到文件的起始位置

  • void rewind ( FILE * stream );
  • 一样的效果fseek实现:fseek(FILE * stream,0,SEEK_CUR);

在这里插入图片描述

四、文本文件与二进制文件简单对比

字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。下面将用数值型数据存储演示

  • 二进制文件:数据在内存中以二进制的形式存储,如果不加转换的输出到外存
  • 文本文件:如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件

如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节(VS2013测试)。

二进制存储

int main()
{
	int a = 10000;

	FILE* pf = fopen("test.txt", "wb");
	if (pf == NULL)
		return 0;

	fwrite(&a, 4, 1, pf); // 以二进制的形式写到文件中
	
	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

文件存储

内容直接显示为整数10000

五、被错误使用的feof

  • int feof( FILE *stream );
  • feof()是检测流上的文件结束符的函数,如果文件结束,则返回非0值,否则返回0

注意:feof 并不是用来判断文件是否结束的,而是又来判断是因为什么原因而结束的(1. 读取失败,2. 读到文件结尾)

文本文件

  • 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
int main()
{
	int c; // 注意:为了判断EOF,需要用int
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		printf("File opening failed");
		return 0;
	}
	//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
	while ((c = fgetc(pf)) != EOF)
	{
		fputc(c, stdout);
	}

	// 判断什么原因结束的
	if (feof(pf))
	{
		printf("End of file reached successfully");
	}
	else
	{
		printf("error when reading");
	}

	fclose(pf);
	pf = NULL;

	return 0;
}

二进制文件

#include <stdio.h>
int main()
{
	int a = 10000;
	int c = 0;
	FILE* pf = fopen("test.txt", "rb");
	if (pf == NULL)
		return 0;

	while (fread(&c, 4, 1, pf))
	{
		printf("%d\n", feof(pf));
	}
	printf("%d\n", feof(pf));
	if (feof(pf))
		printf("End of file reached successfully\n");
	else
		printf("error when reading\n");

	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述

六、文件缓冲区

什么是文件缓冲区

ANSIC 标准提出的“文件缓冲区系统”,所谓缓冲文件系统是指系统自动地在内存中为程序 中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓 冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。未满前,可刷新缓冲区,让其强制输出,输入。缓冲区的大小设计是由编译器的厂商确定的

在这里插入图片描述

  • 注意:在Linux中能更好的看出缓冲区的效果
#include <stdio.h>
#include <windows.h>
//VS2013 WIN10环境测试
int main()
{
	FILE* pf = fopen("test.txt", "w");
	fputs("abcdef", pf);//先将代码放在输出缓冲区
	printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
	Sleep(10000);
	printf("刷新缓冲区\n");
	fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
	//注:fflush 在高版本的VS上不能使用了
	printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");
	Sleep(10000);
	fclose(pf);
	//注:fclose在关闭文件的时候,也会刷新缓冲区
	pf = NULL;
	return 0;
}
结论

因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文 件。 如果不做,可能导致读写文件的问题。

缓冲区的意义

  1. 把用户输入的指令一起处理 比 一个字符一个字符的处理 消耗更小
  2. 当用户在输入时,输入错误,可更改,回车后才执行,更好的用户体验感

缓冲区分类

C语言在不同的地方根据需要使用不同的缓冲区。
缓冲区分为两类:完全缓冲I/O和行缓冲I/O和不带缓冲。
(1)完全缓冲是指的是当前缓冲区被填满时才刷新缓冲区(内容被发送至目的地),通常出现在文件输入中。缓冲区的大小取决于系统,常见的大小是512字节和4096字节。
(2)行缓冲指的是在出现换行时刷新缓冲区。键盘输入通常是行缓冲输入,所以在按下Enter键时才刷新缓冲区。常见的例子就是getchar()函数,当程序调用getchar()函数时,程序就等着用户按键,用户输入的字符被存放在键盘缓冲区中,直到用户按回车为止(回车字符也放在缓冲区中)。当用户键入回车之后,getchar()函数才开始从键盘缓冲区中每次读入一个字符。也就是说,后续的getchar()函数调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完后,才重新等待用户按键。当缓冲区满时不会自动刷新且无法继续输入,需要按回车才可以刷新缓冲区。
(3)不带缓冲:标准输出不带缓冲(例如:cerr
(4)缓冲区的刷新(执行真正的I/O操作)
当缓冲区满时:执行flush语句;执行end语句;关闭文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值