C语言:语言文件操作

目录

  • 1.为什么使用文件
  • 2.什么是文件
    • 2.1 程序文件
    • 2.2 数据文件
    • 2.3 文件名
  • 3.文件的打开和关闭
    • 3.1文件指针
    • 3.2 文件的打开和关闭
  • 4.stream——流
  • 5.文件的顺序读写
    • 5.1字符输出函数——fputc
    • 5.2字符输入函数——fgetc
    • 5.3文本行输出函数——fputs
    • 5.4文本行输入函数
    • 5.5格式化输出函数——fprintf
    • 5.6格式化输入函数——fscanf
    • 5.7二进制输出——fwrite
    • 5.8二进制输入函数——fread
    • 对比一组函数
  • 6.文件的随机读写
    • 6.1 fseek
    • 6.2 测量文件偏移量函数——ftell
    • 6.3恢复指针位置函数——rewind
  • 7.文本文件和二进制文件
  • 8.文件读取结束的判定
    • 8.1被错误使用的feof
  • 9.文件缓冲区

1.为什么使用文件

我们可以将自己写好的各种文件和项目生成的数据存放在文件中,等下次再去运行程序时,存放在文件的数据就可以加载到该项目中,不必一开始将数据多次录入。

我们在想需要存储数据的项目和程序就应该把信息记录下来,只有我们自己选择删除数据的时候,数据才不复存在。这就涉及到了数据持久化的问题,我们一般数据持久化的方法有,把数据存放在磁盘文件、存放到数据库等方式。

使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。




2.什么是文件

查看文件类型时要打开文件扩展名显示
在这里插入图片描述
在程序设计中,我们一般谈的文件有两种:程序文件、数据文件

2.1 程序文件

包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)

2.2 数据文件

文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
C语言:语言文件操作_第1张图片
在以前所处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。

2.3 文件名

一个文件要有一个唯一的文件标识,以便用户识别和引用。
文件名包含3部分:文件路径+文件名主干+文件后缀
例如: c:\code\test.txt
为了方便起见,文件标识常被称为文件名。

相对路径:就是相对于自己的目标文件的位置。(指以当前文件所处目录而言文件的位置)————以引用文件之间网页所在位置为参考基础,而建立出的目录路径。因此当保存于不同目录的网页引用同一个文件时,所使用的路径将不相同,故称之为相对。
例如:c:\code\file\test.txt这个文件,假如我此时在c:\code文件下,对于test.txt文件的相对路径就是file\test.txt
C语言:语言文件操作_第2张图片

绝对路径:是指文件在硬盘上真正存在的路径。(指对站点的根目录而言某文件的位置)
C语言:语言文件操作_第3张图片



3.文件的打开和关闭

3.1文件指针

缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统
声明的,取名FILE


VS2013对FILE的定义,不同的编译器对FILE的定义不一样,这里只是拿VS2013举例

struct _iobuf {
	char *_ptr;
	int _cnt;
	char *_base;
	int _flag;
	int _file;
	int _charbuf;
	int _bufsiz;
	char *_tmpfname;
};
typedef struct _iobuf FILE;

创建一个FILE*的指针变量:

FILE* pf;//文件指针变量

定义pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件。
C语言:语言文件操作_第4张图片
C语言:语言文件操作_第5张图片

3.2 文件的打开和关闭

文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
打开文件用fopen函数,关闭文件用fclose函数

//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream );

fopen函数的filename指的是文件名,(文件的默认路径是.c文件所在的目录下),如果当前文件不在文件默认路径下就只能去输入文件的绝对路径
C语言:语言文件操作_第6张图片
fopen函数的mode参数指的是,下面的文件使用方式,这里是char*类型,指的是字符串所以要用双引号
C语言:语言文件操作_第7张图片

如果指定文件不存在,建立一个新的文件是指,假如你所输入的文件路径没有指定的文件,会帮你创建一个你所指定的文件
如果文件不存在,错误,指的是如果文件不存在会报错

“w”只写,假如文件也对应的创建好了,当里面有信息时,我们要是要在其中写入的话,会先将原信息删除,再去写入我们想写入的信息

“r”只读,如果在默认文件下没有指定文件会返回NULL空指针,如果有文件打开失败会报错

//以只读的方式打开文件
	FILE* pf= fopen("test.txt", "r");
	if (pf = NULL)
	{
		perror("fopen");
		return;
	}

	//读文件
	//...
	

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

如果想访问其他路径下的文件就要输入绝对路径去打开
例:

//以只读的方式打开文件
						//输入绝对路径
	FILE* pf= fopen("C:\\Users\\Desktop", "r");
	//这里写双\\是因为多加一个\变转义字符,代表一个反斜杠字符'\',单个反斜杠会被误认
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}

	//读文件
	//...
	

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




4.stream——流

介绍下面的内容前,我们先去介绍一下流是什么
流:高度抽象概念
因为程序会跑在各个存储内容和各个设备上,如果让一个人去记住如何存储各个设备的方法实在过于麻烦
C语言:语言文件操作_第8张图片

不同的设备内容和机制都不相同,所以用流去封装后,所有设备用对应的流去访问,就可以变得很方便。
C语言:语言文件操作_第9张图片
注意
C语言程序只要运行起来,就默认打开了3个流

名称 功能 作用对象
stdin 标准输入流 键盘
stdout 标准输出流 屏幕
stderr 标准错误流 屏幕




5.文件的顺序读写

C语言:语言文件操作_第10张图片

5.1字符输出函数——fputc

字符输出函数:fputc

功能:将字符写入到指定的流中

int fputc( int c, FILE *stream );
先输入要输的字符,再去输入要写入的流,以int的形式返回输入的字符,错误时返回EOF

//以只写的方式打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}

	//写文件
	fputc('a', pf);
	fputc('b', pf);
	fputc('c', pf);

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

C语言:语言文件操作_第11张图片


5.2字符输入函数——fgetc

字符输入函数:fgetc

功能:在指定的流中读取其中的字符

int fgetc( FILE *stream );
输入要读取的流,以int的形式返回读取的字符,错误或文件结束时返回EOF

下面利用fgetc的返回值可以以此将文件中的每个字符都读取出来

#include
int main()
{
	//以只读的方式打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	//读文件
	char ch=0;
	while ((ch = fgetc(pf))!=EOF)
	{
		printf("%c", ch);
	}

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

C语言:语言文件操作_第12张图片




5.3文本行输出函数——fputs

文本行输出函数:fputs

功能:输入字符串到对应的流中

int fputs( const char *string, FILE *stream );
输入要写入的字符串,输入要写入信息的流
如果成功,函数都会返回一个非负值。在出现错误时,fputs返回EOF

int main()
{
	//以只写的方式打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}

	//写文件
	fputs("abcdefd", pf);
	fputs("xxxxxxx", pf);
	
	//关闭文件
	fclose(pf);
	pf = NULL;
}

C语言:语言文件操作_第13张图片
是否换行取决于你是否写了\n




5.4文本行输入函数

文本行输入函数:fgets

功能:以字符串的形势读取流中的信息

char *fgets( char *string, int n, FILE *stream );
输入要存储字符串的载体,输入要读取的字符数量,输入流,返回NULL以指示错误或文件结束条件

fgets每次只能最多读取n-1个内容,因为字符串后面需要\0所以如果输入255其实最多只能读取254个内容

它和fputs的不同是虽然都叫行输入输出,但是fgets是标准的一行一行的去读取,而fputs不写\n不会自动跳转行

int main()
{
	//以只读的方式打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}

	//读文件
	char arr[256] = { 0 };
	fgets(arr,255,pf);
	printf("%s", arr);
	fgets(arr,255,pf);
	printf("%s", arr);
	//关闭文件
	fclose(pf);
	pf = NULL;
}

C语言:语言文件操作_第14张图片
也可以用while循环以次读取每一行

int main()
{
	//以只读的方式打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}

	//读文件
	char arr[256] = { 0 };
	while(fgets(arr,255,pf)!=NULL)
	{
		printf("%s", arr);
	}
	
	//关闭文件
	fclose(pf);
	pf = NULL;
}




5.5格式化输出函数——fprintf

格式化输出函数:fprintf

功能:把格式化(不同类型)的数据,转换成字符,写入指定流中

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

如果看不懂fprintf的参数可以对比着printf去看,会发现fprintf后面的参数和printf的参数一样,第一个参数是流,这样就知道怎么去使用fprintf了
C语言:语言文件操作_第15张图片
C语言:语言文件操作_第16张图片

我们尝试将一个结构体写入文件流中,结构体可以创建多个类型

struct Student
{
	char name[20];
	int year;
	float score;
};

int main()
{
	struct Student s = { "张三",19,88.0 };
	//以只写的方式打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}

	//写文件
	fprintf(pf, "%s %d %f", s.name, s.year, s.score);
	
	//关闭文件
	fclose(pf);
	pf = NULL;
}

C语言:语言文件操作_第17张图片



5.6格式化输入函数——fscanf

格式化输入函数:fscanf

功能:从指定的流中以字符串的形式读取出格式化的数据,并放入指定的变量中

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

如果看不懂fscanf的参数可以对比着scanf去看,会发现fscanf后面的参数和scanf的参数一样,第一个参数是流,这样就知道怎么去使用fscanf了
C语言:语言文件操作_第18张图片
C语言:语言文件操作_第19张图片

struct Student
{
	char name[20];
	int year;
	float score;
};

int main()
{
	struct Student s = { 0 };
	//以只读的方式打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}

	//读文件
	fscanf(pf, "%s %d %f", s.name, &(s.year), &(s.score));
	printf("%s %d %f", s.name, s.year, s.score);

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

C语言:语言文件操作_第20张图片




5.7二进制输出——fwrite

二进制输出:fwrite

功能:将buffer对应数据以2进制的方式存储到指定流中

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

struct Student
{
	char name[20];
	int year;
	float score;
};

int main()
{
	struct Student s = { "张三",19,88.0 };
	//wb是以二进制的方式写入
	FILE* pf = fopen("text.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//以二进制的方式写文件
	fwrite(&s,sizeof(struct Student),1, pf);

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

当你写入后打开text.txt文件后你会发现是乱码,是因为我们是以二进制的形式写入的信息,而记事本是以文本的形式打开的文件,所以是乱码
C语言:语言文件操作_第21张图片



5.8二进制输入函数——fread

二进制输入:fread

功能:将流中的指定数据取出到buffer

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

struct Student
{
	char name[20];
	int year;
	float score;
};

int main()
{
	struct Student s = { 0 };
	//以二进制的方式读取
	FILE* pf = fopen("text.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//以二进制的方式读文件
	fread(&s,sizeof(struct Student),1, pf);

	printf("%s %d %f", s.name, s.year, s.score);

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

C语言:语言文件操作_第22张图片

对比一组函数

scanf/fscanf/sscanf
printf/fprintf/sprintf

C语言:语言文件操作_第23张图片

函数 功能
scanf 针对标准输入(键盘)格式化的输入语句——stdin
printf 针对标准输出的格式化输出语句——stdout
fscanf 针对所有输入流的格式化的输入语句
fprintf 针对所有输出流的格式化输出语句
sprintf 从一个字符串中读取一个格式化的数据
sscanf 把一个格式化的数据转换成字符串




6.文件的随机读写

前面所介绍的一些函数都不能指定位置去读写信息,下面来介绍一下怎样去随机读写想要的信息

6.1 fseek

功能:根据文件指针的位置和偏移量来定位文件指针
注意fseek本身并没有读写功能,是指修改文件指针的偏移量


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


stream指的是流,offset指的是偏移量
origin是起始位置
起始位置分三个,需要时输入SEEK_CUR,SEEK_END,SEEK_SET

SEEK_CUR偏移量可以为正也可以为负
C语言:语言文件操作_第24张图片

下面举例说明使用fseek去读,seek(寻找)
text.txt文件中有abcdef这几个字符,当我们读取出一个字符后,指针就会向后偏移一个位置,我们读取两次后,指针来到c前面,但是fseek(pf, 2, SEEK_CUR);这句话会让当前指针向后偏移两个单位,最后再去读取时读取到的就是e了
C语言:语言文件操作_第25张图片


例如:如果替换成fseek(pf, -1, SEEK_END);虽然读完a b后,指针指向c,但运行fseek后又要回到最尾端的位置,这里偏移量为-1,就是向前进一位偏移量,最后指向f,最后读取的是abf


例如:如果替换成fseek(pf,4,SEEK_SET);虽然读完a b后,指针指向c,但运行fseek后又要回到文件起始位置,这里偏移量为4,就是从文件起始位置向后偏移4个位置,最后指向d,最后读取的是abd


**如果越出文件的范围,则返回空**

C语言:语言文件操作_第26张图片


下面举例说明使用fseek去写入
先将xxxxx写入后再将指针从起始位置向后拨动3个偏移量后再写入abc
C语言:语言文件操作_第27张图片



6.2 测量文件偏移量函数——ftell

测量文件偏移量函数:ftell

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

long ftell( FILE *stream );
C语言:语言文件操作_第28张图片


6.3恢复指针位置函数——rewind

void rewind ( FILE * stream );

功能:让指针的位置回到文件的起始位置
C语言:语言文件操作_第29张图片




7.文本文件和二进制文件

数据文件被称为文本文件或者二进制文件
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。

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

如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节(VS2013测试)
C语言:语言文件操作_第30张图片
我们以二进制的方式打开一个二进制文件

#include 
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
fwrite(&a, 4, 1, pf);//二进制的形式写到文件中
fclose(pf);
pf = NULL;
return 0;
}

C语言:语言文件操作_第31张图片
由于是VS是小端存储最后是10 27 00 00
C语言:语言文件操作_第32张图片
但你使用文本方式打开是乱码
C语言:语言文件操作_第33张图片



8.文件读取结束的判定

8.1被错误使用的feof

头文件:stdio.h

牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。
而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束

feof的使用

#include 
#include 
int main(void)
{
	int c; // 注意:int,非char,要求处理EOF
	FILE* fp = fopen("test.txt", "r");
	if(!fp) 
	{
		perror("File opening failed");
		return ;
	}
	//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);
	fp==NULL;
}




9.文件缓冲区

从内存中程序到硬盘进行交互时要经过文件缓冲区

是因为要是每次输入信息都打断cpu使其交互的话,就会多次打断cpu,这里设计一个文件缓冲区,当文件缓冲区的数据达到一定数量时,cpu就会把程序的数据交互到硬盘中


下面设计的程序时验证缓冲区的存在,当我们写入数据到硬盘时,首先要放到文件缓冲区,当放入后我们将程序睡眠,打开文件会发现文件里没有写入任何信息,然后再去刷新缓冲区将数据都写入到硬盘的文件中,fflush函数可以将文件缓冲区的数据输出到硬盘中,再去睡眠打开文件会发现信息输出到了硬盘文件中,这样就可以验证文件缓冲区的存在

#include 
#include 
//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语言,c语言,c++,开发语言,算法)