文件

在程序设计中,一般将文件分为两类:程序文件和数据文件

程序文件:源程序文件(后缀为.c文件)、目标文件(windows系统下后缀为为OBJ文件)、可执行文件(windows系统下后缀为为exe文件)

数据文件:文件内容不一定时程序,是程序运行时需要读写的数据。如程序运行时需要读取其中数据或者输出内容

文件名

一个文件要有独特的文件标识(文件标识也称文件名)才能方便被使用和寻找。通常文件名组成包含:文件路径+文件名主干+文件后缀

文件类型

根据数据的组织形式,文件可以分为:文本文件和二进制文件。首先数据在内存中的存储形式是二进制序列,当数据在内存中不加转换直接输出到外存(硬盘),那么这种文件就是二进制文件;如果数据在输出到外存前经过转换为ASCII值存储到外存,则这类文件称为文本文件。

文件缓冲区

ANSIC标准采用“文件缓冲系统”来处理数据文件,文件缓冲系统就是系统会在内存上自动为正在执行的文件开辟一段内存充当“文件缓冲区”。如将数组存储到硬盘中会先将数据存储到文件缓冲区,等到文件缓冲区的数据达到一定量后再存入硬盘。

文件指针

每当我们使用一个文件时,系统就会创造一个文件信息区用来存放文件的有关信息。而这些信息都是存储在一个结构体中的,并且系统给这个结构体类型起名为FILE。所以我们每次打开一个文件时,系统都会创造一个FILE类型的结构体变量来存储文件的信息。一般使用一个FILE类型的文件指针来维护文件的信息。

操作文件函数

fopen函数

FILE* fopen( const char* filename, const char* mode )

这个函数用于打开文件,它的第一个参数是文件路径名,它有两种写法:相对路径和绝对路径

1.绝对路径就是那个文件的准确地址,如C:..........

2.相对路径可以有两种形式,1是文件就再.c文件的同一个文件夹中,可以直接写文件名不加路径2.是文件在.c文件的上一层文件中,可采用第二中方法。

#include<errno.h>
int main()
{
	FILE* p = fopen("c.txt", "r");//相对路径1.文件在.c文件同一层,不加路径
	//FILE* p = fopen("../c.txt", "r");//相对路径2.文件在.c文件上一层
	//FILE* p = fopen("../../c.txt", "r");//相对路径2.文件在.c文件上两层
	//FILE* pf = fopen("D:\C\c.txt", "r");绝对路径
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	fclose(p);
	p = NULL;
	return 0;
}

第二个参数是打开文件的模式

名称 含义
"r" 为了输入数据,打开一个存在的文本文件,若不存在,报错
"w" 为了输出数据,打开一个文本文件,若不存在,创建
"a" 为了向文本文件尾添加数据,若不存在,报错
"rb" 为了输入数据,打开一个的二进制文件,若不存在,报错
"wb" 为了输出数据,打开一个二进制文件,若不存在,创建
"ab" 为了向二进制文件尾添加数据,若不存在,报错
"r+" 为了读和写打开一个文本文件,若不存在,报错
"w+" 为了读和写新建一个文件,若不存在,创建
"a+" 打开一个文件,在文件尾进行读写,若不存在,创建
"rb+" 为了读和写打开一个二进制文件,若不存在,报错
"wb+" 为了读和写新建一个二进制文件,若不存在,创建
"ab+" 打开一个二进制文件,在文件尾进行读写,若不存在,创建

这里的w类输出数据是覆盖写,会覆盖掉原来的内容,即无论是否存在源文件都会新建一个文件。若想增加内容就用追加数据。

这个函数会返回指向文件信息区的结构体指针FILE

flcose函数

int fclose( FILE* stream )

这个函数用于关闭打开的文件,参数就是指向文件信息区的指针。它的使用方法如下

	FILE* pf = fopen("D:\C\c.txt", "r");
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	fclose(p);
	p = NULL;
fgetc函数

int fgetc( FILE* stream )

这个函数用于从输入流中读取字符,它返回读取字符的ASCII值,它适用于所有输入流,包括标准输入流stdin。若反复读取它读取到的字符会按顺序往后移动。

char a = fgetc(stdin);//将键盘输入的字符读入存储到变量a中
fputc函数

int fputc( int c, FILE* stream)

这个函数用于将字符c写入到输出流,它适用于所有输出流,包括标准输出stdout。

fputc(a, stdout);//将a字符输出到屏幕
fgets函数

char* fgets( char* string, int n, FILE* stream )

这个函数用于读取输入流中的一行字符。它的第一个参数是用于存储第一行字符的字符串数组,第二个参数是最多读取的字符,第三个参数是输入流。它的返回值是用于接收的字符串数组的地址。注意:当文件中内容有换行时,读取函数也会读取到换行符\n。puts函数无论一行字符末尾有无换行都会添加换行符

	char arr[1024] = { 0 };
	FILE* p = fopen("c.txt", "r");
	if (p == NULL)
	{
		return 0;
	}
	fgets(arr, 1024, p);//读取第一行数据
	printf("%s", arr); 
	fgets(arr, 1024, p);//读取第二行数据
	printf("%s", arr);
	fgets(arr, 1024, p);//读取第三行数据
	printf("%s", arr);

	fclose(p);
	p == NULL;
fputs函数

int fputs( const char* string, FILE* stream )

这个函数用于写一行数据到输出流,它的第一个参数是写的数据存储的字符串,与puts函数类似

 	fgets(arr, 1024, stdin);
	fputs(arr, stdout);
fprintf函数

int fprintf( FILE *stream, const char *format [, argument ]...)

格式化输出函数,它的使用对应所有输出流,用于写入数据到文件中

fscanf函数

int fscanf( FILE *stream, const char *format [, argument ]... )

格式化输入函数,它可以适用于所有输入流,可用于将数据读取输入流中的数据

int main()
{
	char arr[1024] = { 0 };
	fscanf(stdin,"%s", arr);//键盘输入
	fprintf(stdout,"%s", arr);//屏幕输出
	return 0;
}
插语:

关于scanf/printf函数和fscanf/fprintf函数和sscanf/sprintf函数的区别

首先是scanf/printf函数是针对标准输入/输出流的格式化输入/输出函数

fscanf/fprintf函数是针对所有输入/输出流的格式化输入/输出函数。fscanf函数用于读取输入流中文件的数据,而fprintf函数利用到输出流写入数据到文件中

sscanf函数int sscanf( const char* buffer, const char* format [, argument ] ... )用于用于将字符串输入为到原来的格式化类型数据。相反sprintf函数int sprintf( char* buffer, const char* format [, argument] ... )将格式化数据输出到前面的数组buffer中

struct S
{
	int a;
	char b;
	char c[10];
};

int main()
{
	struct S s = { 8,'a',"peoep" };
	char buf[100] = { 0 };
	sprintf(buf, "%d%c%s",s.a,s.b,s.c);
	sscanf(buf,"%d %c %s",&(s.a),&(s.b),s.c);
	return 0;
}
fread、fwrite函数

size_t fread( void* buffer, size_t size, size_t count, FILE* stream );

size_t fwrite( const void* buffer, size_t size, size_t count, FILE* stream );

fwrite函数的作用是将数据以二进制的形式写入文件中,其中他的第一个参数是写入的数据的指针,第二个参数是每份数据的大小,第三个数据是数据的份数,第四个数据是输出流

fread函数的作用是将以二进制形式存储在文件中数据读出来,其中它的第一个参数是指向存放读出来的数据的空间,中间两个参数与fwrite一样,最后一个参数是输入流

#include <errno.h>
struct S
{
	int a;
	char b;
	char c[10];
};
int main()
{
	//struct S s = { 10,'a',"王五" };
	struct S tmp = {0};
	FILE * ps = fopen("D:\\visual studio code\\Test1\\Test1\\c.txt","rb");
	if (ps == NULL)
	{
		printf("%s", strerror(errno));
		return 0;
	}
	//fwrite(&s,sizeof(struct S),1,ps);
	fread(&tmp,sizeof(struct S),1,ps);
	printf("%d  %c  %s",tmp.a,tmp.b,tmp.c);
	
	
	fclose(ps);
	ps == NULL;
	return 0;
}
fseek函数

int fseek( FILE* stream, long offset, int origin )

这个函数可以用来查找文件里面任意位置的元素,他有三个参数,第一个是文件指针,第二个是地址偏移量,第三个是文件指针的位置,它有三种可能,SEEK_CUR当前位置、SEEK_SET文件指针起点、SEEK_END文件指针终点。

	//文件内字符为abcdef
	char tmp = 0;
	FILE* ps = fopen("D:\\visual studio code\\Test1\\Test1\\c.txt", "r");
	if (ps == NULL)
	{
		printf("%s", strerror(errno));
		return 0;//不可少,不然fseek报警告
	}
	fseek(ps,2,SEEK_CUR);
	fscanf(ps, "%c",&tmp);
	printf("%c", tmp);
	fclose(ps);
	ps = NULL;
	return 0;
ftell函数

long ftell( FILE* stream )

这个函数会返回文件指针相对于起始位置的偏移量

	int position=ftell(ps);
	printf("%d\n", position);//偏移量为2
rewind函数

void rewind( FILE* stream )

这个函数用于将文件指针置回到初始位置

	fseek(ps,2,SEEK_CUR);
	rewind(ps);
feof函数

int feof( FILE* stream )

这个函数用于判断程序读取文件结束的原因,如果是正常读取到末尾结束(fgetc是EOF、fgets是NULL)则返回非0值,要是在读取文件中途就结束失败,则返回一个0

ferror函数

int ferror( FILE* stream )

这个函数在文件读取操作完成后,用来判断文件读取是否有错误(即中途就停止),如果文件读取过程中有错误导致没读完文件,则返回1,如果没有错误,返回0

perror函数

void perror( const char* string )

这个函数用来给错误的原因,与strerror函数类似,不过它更加灵活。它可以直接将错误原因打印在屏幕,它的参数是一串可以打印在错误原因前的自定义字符串。