【C语言】详解文件操作


(零)引入

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

 

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

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

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

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

零.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;
}

 

完~
 

  • 17
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水墨不写bug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值