C语言_文件操作

本篇博客用于整理和介绍C语言中文件操作的相关知识,主要介绍基本的文件读写函数,并且举例演示如何使用这些函数。

文件的基本定义

在程序设计中,我们一般谈到的文件分为程序文件数据文件

程序文件:包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。
数据文件:文件内容不是程序,而是程序运行时读写的数据。比如说我们输入通讯录的联系人的信息。

这篇博客中所说的文件操作,指的是对数据文件的读写操作。

文件名

文件名包含3部分:文件路径+文件名主干+文件后缀
例如:c:\code\test.txt

在这里插入图片描述
一般在某个文件夹下,能够区分文件的文件主干+文件后缀就是文件名,比如说在C盘的code文件夹中,test.txt就是通常认为的文件名,但实际上应该要包括文件路径。

数据文件的分类

数据文件一般分为文本文件二进制文件

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

以ASCII字符型形式存储就指的是将数据看成字符,然后以字符的ASCII码值存储。

文件类型的指针

数据文件的处理是采用“缓冲文件系统”来处理数据文件的。系统会自动给程序中正在使用的数据文件在内存上开辟一个缓冲区,程序输出时将数据输入缓冲区,缓冲区满后输出到磁盘;程序输入时先从磁盘上读取一批数据放入内存,然后再根据需要一个个输入到程序数据中。这样做可以提高数据读写效率,因为内存上的数据读写速度更快。
缓冲文件系统中,每个正在使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息,这些信息存放在一个结构体(FILE)中,在这里我们不用过分关注这些信息都是什么,我们的重点是如何进行文件读写。总结就是:我们进行操作的文件是FILE类型,我们在操作文件的时候创建一个FILE*指针来操作

文件操作

在了解上述文件基本概念的基础之上,我们就可以进行文件操作了。

文件的打开、关闭和创建

文件读写的重要步骤就是打开和关闭,我们在使用文件时打开文件,在使用结束后一定要关闭文件。

文件的打开函数:
FILE *fopen( const char *filename, const char *mode )

参数:filename是文件名,mode是打开模式
返回值:FILE*类型的指针。文件打开或者创建之后,返回指向文件的指针,相当于建立了与该文件的联系;如果返回空指针说明出错。
作用:打开或者建立一个文件

下表是mode参数的选择:

文件使用方式含义如果指定文件不存在
“r”(只读)为了输入数据,打开一个已经存在的文本文件出错
“w”(只写)为了输出数据,打开一个文本文件建立一个新的文件
“a”(追加)向文本文件尾添加数据出错
“rb”(只读)为了输入数据,打开一个二进制文件出错
“wb”(只写)为了输出数据,打开一个二进制文件建立一个新的文件
“ab”(追加)向一个二进制文件尾添加数据出错
“r+”(读写)为了读和写,打开一个文本文件出错
“w+”(读写)为了读和写,建议一个新的文件建立一个新的文件
“a+”(读写)打开一个文件,在文件尾进行读写建立一个新的文件
“rb+”(读写)为了读和写打开一个二进制文件出错
“wb+”(读写)为了读和写,新建一个新的二进制文件建立一个新的文件
“ab+”(读写)打开一个二进制文件,在文件尾进行读和写建立一个新的文件

在要打开的文件未创建的时候,选择合适的参数可以建立一个新的文件,所以说文件的创建也是用fopen函数来实现的。

文件关闭函数:
int fclose( FILE *stream );

参数: stream指的是指针变量
返回值: 关闭成功返回0,失败返回EOF
作用:关闭文件,将文件数据写入磁盘,可以防止文件一直被独占打开,防止数据丢失。

文件顺序读写

功能函数名适用于
字符输入函数fgetc所有输入流
字符输出函数fputc所有输出流
文本行输入函数fgets所有输入流
文本行输出函数fputs所有输出流
格式化输入函数fscanf所有输入流
格式化输出函数fprintf所有输出流
二进制输入fread文件
二进制输出fwrite文件

关于上述函数的命名需要解释一下:这块的输入指的是从磁盘到内存的输入(或者是从键盘到内存的输入),输出指的是从内存到磁盘的输出(或者是从内存到终端的输出)。举例来说,文件中的数据是放在磁盘上的,程序产生的数据在内存上,最终我们要将数据输出到磁盘中,要是想得到数据,那么就要从磁盘输入到内存中。
总结:输入输出的对象是内存。输入是往内存中输入,输出是从内存中输出。

fputc()

函数原型:int fputc( int c, FILE *stream );
参数: c指的是字符,stream是指向要操作的文件的指针变量
返回值:int型的字符(字符的ASCII值),获取失败了返回EOF
作用:在文件中写入一个字符。(从内存往文件输出一个字符)

// 文件操作

int main()
{
	FILE* pf = fopen("test.txt", "w"); // 以只写的方式打开文件test.txt,如果文件未定义,那么创建该文件再写入
	if (pf == NULL)  // 判断是否打开成功
	{
		perror("fopen");
		return -1;
	}

	// 写文件
	fputc('a', pf);
	fputc('b', pf);
	fputc('c', pf);
	fputc('d', pf);
	fputc('e', pf);
	fputs("\nHello world!\n",pf);

	// 关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述

fgetc()

函数原型:int fgetc( FILE *stream );
参数: stream是指向要操作的文件的指针变量
返回值:int型的字符(字符的ASCII值),获取失败了返回EOF
作用:读取文件中的一个字符。(从文件往内存中输入一个字符)

举例:对数据进行顺序读取
还是对上面创建的test.txt文件进行操作`

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return -1;
	}

	// 读文件
	int  ch = fgetc(pf);
	printf("%c\n", ch);
	ch = fgetc(pf);
	printf("%c\n", ch);
	ch = fgetc(pf);
	printf("%c\n", ch);
	ch = fgetc(pf);
	printf("%c\n", ch);
	ch = fgetc(pf);
	printf("%c\n", ch);

	// 关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

上述代码是对test.txt文件中的内容进行顺序读取,是从文件中的第一个字符开始一个一个往后读取。

fputs()

函数原型: int fputs( const char *string, FILE *stream );
参数:string是要写入到文件中的字符串的指针变量;stream是指向要操作的文件的指针变量
返回值:成功返回一个非负整数;出错返回EOF
作用:在文件中写入一行字符。(从内存往文件中输出一行字符)

举例:在文件中写入两行字符

int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return -1;
	}
	
	//写入数据时,需要将fopen第二个参数为"w"
	fputs("hello, world\n", pf); // 写一行
	fputs("hello, world\n", pf);
	
	// 关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述

fgets()

函数原型:char *fgets( char *string, int n, FILE *stream );
参数:与fputs的区别是多了一个参数n,这参数是要读取的最大字符数
返回值:返回指向读取到的字符串的指针;返回NULL以指示错误或遇到文件结束条件
作用:从文件中读取n个字符。(文件中的数据输入到内存中)

举例:

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return -1;
	}
	
	char str[20] = { 0 };
	
	fgets(str, 13, pf); 
	printf("%s\n", str);
	
	// 关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

在上面的代码中,hello, world总共12个字符,因为读取结果是以字符串形式返回,所以最后面需要加'\0',因此我们要读取所有字符的话fgets()函数的第二个参数应该写13。

fprintf()

从内存格式化输出(格式化写入文件)

函数原型:int fprintf( FILE *stream, const char *format [, argument ]...);
参数:stream是指向要写入的文件的指针;format是格式控制字符串;argument选择性参数
返回值:成功返回写入的字节数;失败返回负值

struct Stu
{
	char name[20];
	int age;
	float score;
};

int main()
{
	struct Stu s = { "zhangsan", 20, 66.5f };
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	// 格式化写入文件
	fprintf(pf, "%s %d %f", s.name, s.age, s.score);

	// 关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述

fscanf()

往内存中格式化输入

函数原型:int fscanf( FILE *stream, const char *format [, argument ]... );
参数:stream是指向要读取的文件的指针;format是格式控制字符串;argument选择性参数
返回值:返回成功转换和分配的字段数;返回值不包括已读取但未分配的字段。返回值0表示未分配任何字段。如果发生错误或者到达文件流的末尾,则返回EOF

struct Stu
{
	char name[20];
	int age;
	float score;
};

int main()
{
	struct Stu s = { 0 };
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	// 格式化输入内存
	fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));
	// 打印
	printf("%s %d %f\n", s.name, s.age, s.score);
	
	// 关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述

文件二进制读写

fwrite()

函数原型:size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
参数:buffer是指向要写数据的存储位置的指针;size是写的项目的单位大小(比如说要读取int型的数据,那么一个单位就是4个字节);count是写入项目的最大个数;stream是指向要写入的文件的指针
返回值:fwrite返回实际写入的完整项目数,如果发生错误,该数字可能小于count。此外,如果发生错误,则无法确定文件位置指针。

举例

struct Stu
{
	char name[20];
	int age;
	float score;
};

// 写文件
int main()
{
	FILE* pf = fopen("test.dat", "wb");
	if (pf == NULL)
	{
		perror("open file for writing");
		return 1;
	}
	struct Stu s = { "张三", 22, 99.5f };
	// 写文件
	fwrite(&s, sizeof(struct Stu), 1, pf);

	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述

fread()

函数原型:size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
参数:buffer是指向要读取数据的存储位置的指针;size是读取的项目的单位大小(比如说要读取int型的数据,那么一个单位就是4个字节);count是读取项目的最大个数;stream是指向要读取的文件的指针
返回值:fread返回实际读取的项目数,如果发生错误或在达到count之前遇到文件结尾,则可能小于count,使用feof或ferror函数区分读取错误和文件结束情况。如果size或count为0,则fread返回0,并且缓冲区内容不变。

举例

struct Stu
{
	char name[20];
	int age;
	float score;
};

// 读文件
int main()
{
	FILE* pf = fopen("test.dat", "rb");
	if (pf == NULL)
	{
		perror("open file for reading");
		return 1;
	}
	struct Stu s = { 0 };
	// 读文件
	fread(&s, sizeof(struct Stu), 1, pf);
	// 打印出来看一下读取结果是否正确
	printf("%s %d %f", s.name, s.age, s.score);

	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述

在文件的任意位置读写

上面讨论的都是对于文件的顺序读写,程序运行时我们每读取一个数据,文件中的指针就会按照顺序往后移动。因此,要想在文件中的任意位置进行读写,我们需要控制文件中的指针移动。

fseek()

函数原型:int fseek( FILE *stream, long offset, int origin );
参数:offset是距离origin的字节数;origin是初始位置有三个可选参数:SEEK_CUR(当前位置)、SEEK_END(文件末尾)、SEEK_SET(文件开头)
返回值:如果成功fseek返回0。否则返回非零值。
作用:根据文件指针的位置和偏移量来定位文件指针

ftell()

函数原型:long ftell( FILE *stream );
作用:返回文件指针相对于起始位置的偏移量

rewind()

函数原型:void rewind( FILE *stream );
作用:让文件指针的位置回到文件的起始位置

举例:演示上面三个函数的功能

int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		return 1;
	}

	fputs("ABEDEFGHIJKLMNOPQRSTUVWXYZ", pf);

	// 首先测试fseek,目标是把指针定位到第10个字母(J)的位置
	fseek(pf, 10, SEEK_SET); // 设置origin为文件开头,偏移量为10

	// 然后测试ftell,目标是获取当前指针和初始位置的偏移量
	int ret = ftell(pf); // 返回指针与初始位置的偏移量
	printf("对于文件开头的偏移量为:%d\n", ret);

	// 然后测试rewind,目标是在文件开头插入“hello ”
	rewind(pf);

	fputs("hello", pf);

    // 关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

结果:
在这里插入图片描述
下图是文件中的最终结果
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值