【C语言】详解文件操作

2023-12-16 22:42:01


(零)引入

????????终端是计算机系统中与用户进行交互的界面。

?

? ? ? ? 在以往的程序中,我们通过终端用键盘输入数据,通过屏幕输出信息。

????????但是,如果我们不想手动低效地输入数据,而是通过文件一次性高效输入;

????????如果我们不想让数据丢失,而是把输出的数据存储到文件中时,

????????就需要使用一种新的操作——文件操作。

零.1什么是文件?


????????文件是计算机系统中的一种数据存储形式;

????????如文本、图像、音频、视频等。


(一)文件?

1.1为什么使用文件?


????????程序运行时,我们写的程序的数据? 是存储在电脑的内存中;如果程序退出,内存会被操作系统回收,于是数据就丢失了;等再次运行程序,无法得到上次程序的数据的。

????????如果要将数据进行持久化的保存,就要使用文件。

1.2文件的特点:?

????????文件可以由应用程序创建、读取、编辑、删除,它们是计算机系统中存储和传输数据的基本单位;

????????文件可以被命名并存储在计算机的存储介质中;

????????文件还可以根据其格式和扩展名来区分不同类型的文件, 如.txt文件是纯文本文件。

1.2.1文件名


????????

?

????????文件名是用来标识和区分文件的名称。它是文件系统中文件的唯一标识符。

? ? ? ? 文件名的作用是以便用户识别和引用。


文件名包含3部分:文件路径+文件名主干+文件后缀
例如: c:\code\test.txt
重点:在C源文件中引用文件名要注意用两个斜杠来转义为一个斜杠

转义字符:"? ?\\? ? ?==? ? ?\? ?"

1.3文件的分类?


????????但是在程序设计中,我们?般谈的文件有两种:程序文件、数据文件。

1.3.1程序文件

????????程序文件是包含计算机程序代码的文件。


????????例如:".c"文件表示C语言程序文件, ".java"文件表示Java程序文件, ".py"文件表示Python程序文件等等。

? ? ? ? ?但是,程序文件不是我们要深入讨论的文件,因为我们是在程序文件中写代码,而进行文件操作需要的文件不是程序文件,而是数据文件。

1.3.2数据文件


? ? ? ? 文件的内容不?定是程序代码,也可以是程序运行时读写的数据,比如一些文件在程序运行时被程序读取,同时接收并存储程序输出的数据?—— 这些提供输入数据和接受输出数据的文件就是数据文件。


? ? ? ? 我们之前 所处理数据的输入输出都是以终端为对象的,即从终端的键盘输?数据,运行结果从终端显示到显示器(屏幕)上。


????????其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件,这就是使用文件的意义。


1.3.2.1 二进制文件和文本文件?


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


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

? ? ? ? 通常情况下,我们无法阅读二进制文件


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

如何形象理解二进制文件与文本文件呢?

? ? ? ? 假如我们将10000 , ‘a’? ?等存入内存中,那么他在内存中是怎么存储的呢?

? ? ? ? 对于字符,由于有ASCII表的存在,无论是二进制形式,还是文本形式,都是一样的;

? ? ? ? 对于数字,就有所不同了,以10000为例,看一看他在内存中是怎么存储的呢?

例子:

? ? ? ? 当以ASCII形式:每一个位都需要一个字节,需要5个字节;

? ? ? ? 当以二进制形式:10000默认是整形,只需要4个字节。

数字在内存中的存储还是有很大不同的。


??到这里,我们理一下思路——>

小结:

?


(二)文件的打开和关闭

?????????为了更好的讲解文件操作,我们先要引入“流”的概念;

2.1什么是“流”


? ? ? ? 我们的程序存在的意义就是处理问题,也就是说:

????????需要程序从外部获取信息,经过处理之后输出信息。但是不同的设备输入信息的渠道是不同的,为了方便程序从外部获取信息,并且输出信息,C语言抽象出流的概念。


????????C程序针对?件、画?、键盘等的数据输?输出操作都是通过流操作的。
?????????般情况下,我们要想向流?写数据,或者从流中读取数据,都是要打开流,然后操作,之后再关闭流。


2.1.1标准流


那为什么我们从键盘输?数据,向屏幕上输出数据,并没有打开流呢?


那是因为C语言程序在启动的时候,默认打开了3个流:


? stdin-标准输?流,在?多数的环境中从键盘输?,scanf函数就是从标准输?流中读取数据。


? stdout-标准输出流,?多数的环境中输出?显?器界?,printf函数就是将信息输出到标准输出流中。

————————标准输入输出流,说白了就是键盘和屏幕。


? stderr-标准错误流,?多数环境中输出到显?器界?。


? ? ? ? 默认打开这三个流后,我们使?scanf、printf等函数就可以直接进?输?输出操作的。


????????stdin、stdout、stderr三个流的类型是: FILE* ,通常称为?件指针。


????????C语?中,就是通过 FILE* 的?件指针来维护流的各种操作的。


2.2文件指针

????????每个被使?的?件都在内存中开辟了?个相应的?件信息区,?来存放?件的相关信息(如?件的名字,?件状态及?件当前的位置等)。这些信息是保存在?个结构体变量中的。该结构体类型是由系统声明的,取名FILE.
????????

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

例如,VS2013提供的 <stdio.h> 头?件中有以上的?件类型申明:

????????不同的C编译器的FILE类型包含的内容不完全相同,但是?同?异。

??????? 每当打开?个?件的时候,系统会根据?件的情况?动创建?个FILE结构的变量,并填充其中的信息,使?者不必关?细节。

? ? ? ? 这个结构体储存了文件的关键信息,我们通过对结构体内对象的操作,间接实现对文件的操作:


2.3文件的打开和关闭


对文件在读写之前应该先打开?件,在使?结束之后应该关闭?件。


????????使用fopen函数打开文件,在打开?件的同时,会返回?个FILE*的指针变量指向该?件,也相当于建?了指针和?件的关系。


ANSIC规定使? fopen 函数来打开?件, fclose 来关闭?件。
?

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

文件的打开有多种方式,mode表??件的打开模式,下?都是?件的打开模式:

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

? ? ? ? “r” “w” “a”?是三个基本的打开方式—— 只写“r” “w” “a”表示打开文本文件, “rb” “wb” “ab”表示打开二进制文件。

???????? “r+” “w+” “a+”???????? “rb+” “wb+” “ab+”是复合的操作,是同时能够读和写的打开方式。


?

2.4文件的读写

? ? ? ? 文件的读写有两种类型:顺序读写与随机读写?

对于顺序读写,C语言提供了标准库函数,他们包含在<stdio.h>中。

?

顺序读写函数介绍

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

从这些函数声明中,我们就能大致了解这些函数的使用方法了;?

?

?

?

?

?

?

?如果想要进一步深入了解,请看Cplusplus.com

(stdio.h) - C++ Reference (cplusplus.com)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/cstdio/


文件的打开方式是前提,读写是操作,文件关闭是习惯,指针置空是与人拉开的差距。

?

????????上?说的适?于所有输?流?般指适?于标准输?流和其他输?流(如?件输?流);所有输出流?般指适?于标准输出流和其他输出流(如?件输出流)

? ? ? ? 为了便于理解,这里给出一些应用实例:

#include<stdio.h>
#include<stdlib.h>

struct re_col
{
	char name[20];
	char us_n[20];
	char us_s[20];
	int age;
};

typedef struct re_col R;

int main()
{
	printf("请输入注册信息>用户名-账号-密码-年龄\n");
	
	FILE* pf = fopen("C:\\Users\\35587\\Desktop\\register collum","w");
	if(pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	
	R* p = (R*)malloc(sizeof(R)*1);
	if(p == NULL)
	{
		perror("malloc");
		return 1;
	}
	
	scanf("%s%s%s%d",p->name,p->us_n,p->us_s,&(p->age));
	
	fprintf(pf,"%s %s %s %d",p->name,p->us_n,p->us_s,p->age);
	
	fclose(pf);
	pf = NULL;
	
	free(p);
	p = NULL;
	
	return 0;
}

? ? ? ? ?根据输入,把信息存放到文件 register collum 中;

#include<stdio.h>
int main1()
{
	FILE* pf = fopen("C:\\Users\\35587\\Desktop\\register collum","r");
	if(pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	
	int ch = fgetc(pf);
	
	if(feof(pf))
	{
		puts("EOF had been reached");
	}
	else if(ferror(pf))
	{
		puts("read failure");
	}
	printf("%d",ch);
	
	fclose(pf);
	pf = NULL;
	return 0;
}

int main2()
{
	FILE* pf = fopen("C:\\Users\\35587\\Desktop\\register collum","aw");
	if(pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	
	fputs("iiiii\n",pf);
	fclose(pf);
	pf = NULL;
	
	return 0;
}

int main3()
{
	FILE* pf = fopen("C:\\Users\\35587\\Desktop\\register collum","a");
	if(pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fputs("abni",pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

int main4()
{
	FILE* pf = fopen("C:\\Users\\35587\\Desktop\\register collum","r");
	if(pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char str[50] = {0};
	fgets(str,6,pf);
	fprintf(stdout,"%s\n",str);
	
	fclose(pf);
	pf = NULL;
	return 0;
}

int main5()
{
	FILE* pf = fopen("C:\\Users\\35587\\Desktop\\register collum","aw");
	if(pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fputs("watink",pf);
	
	fclose(pf);
	pf = NULL;
	
	return 0;
}

int main6()
{
	FILE* pf = fopen("C:\\Users\\35587\\Desktop\\register collum","r");
	if(pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	
	char str1[50];
	
	fscanf(pf,"%s",str1);
	printf("%s\n",str1);//检验是否读取成功
	
	fclose(pf);
	pf = NULL;
	return 0;
}

int main7()
{
	FILE* pf = fopen("C:\\Users\\35587\\Desktop\\register collum","w");
	if(pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fprintf(pf,"abcded\n");
	fclose(pf);
	pf = NULL;
	return 0;
}

int main()
{
	FILE* pf1 = fopen("C:\\Users\\35587\\Desktop\\register collum","r");
	if(pf1 == NULL)
	{
		perror("fopen_1");
		return 1;
	}
	FILE* pf2 = fopen("data_copy.txt","w");
	if(pf2 == NULL)
	{
		fclose(pf1);
		pf1 = NULL;
		perror("fopen_2");
		return 1;
	}
	//op
	char ch;
	while((ch = fgetc(pf1)) != EOF)
	{
		fputc(ch,pf2);
	}
	
	fclose(pf1);
	pf1 = NULL;
	fclose(pf2);
	pf2 = NULL;
	return 0;
}

?



2.5?件的随机读写

? ? ? ? 随机读写,实际意义是我们可以控制读写的位置,而不是真的“随机”。?


fseek


根据?件指针的位置和偏移量来?定位??件指针。


ftell


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


rewind


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


?对rewind的应用实例:


int main()
{
	FILE* pf = fopen("C:\\Users\\35587\\Desktop\\data.txt","a+");
	if(pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char str[50];
	fgets(str,6,pf);
	printf("%s\n",str);
	int c = ftell(pf);
	printf("%d\n",c);
	rewind(pf);//回到起始位置

	fputs("acccsffe\n",pf);
	int c1 = ftell(pf);
	printf("%d\n",c1);
	
	fclose(pf);
	pf = NULL;
	
	return 0;
}

?

完~
?

文章来源:https://blog.csdn.net/2301_79465388/article/details/135032679
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。