首页 > 其他分享 >[ c ] 文件操作详解———通讯录

[ c ] 文件操作详解———通讯录

时间:2023-03-22 10:38:28浏览次数:48  
标签:文件 指针 详解 pf 通讯录 test txt con

本文主要介绍了C语言中有关文件的基础知识与基本操作,对涉及到的操作函数进行了详细解释,同时以笔者自己的理解,对函数的命名进行了一些探究,并以通讯录为例,做了打开存储等演示。

一、文件操作

1、文件指针FILE

亦称“文件类型指针”,由此可知,和int,char等类型一样,FILE也是一种数据类型,其中保存着有关文件的相关信息,文件名、状态、存储位置等信息,在vs2022中查看FILE定义可得如下信息:image.png在此我们得知,FILE类型也是一种结构体类型,既然vs2022中没找到具体定义,我们度娘可知: 在vs2013,FILE结构定义如下:

#ifndef _FILE_DEFINED
struct _iobuf {
     char *_ptr;       //文件输入的下一个位置。
     int   _cnt;        //当前缓冲区的相对位置。
        char *_base;       //指针的基础位置(即是文件的起始位置)。
        int   _flag;       //文件标志。
        int   _file;       //文件的有效性验证。
        int   _charbuf;    //检查缓冲区状况,如果无缓冲区则不读取。
        int   _bufsiz;     //缓冲区的大小。
        char *_tmpfname;    //临时文件名。
        };
typedef struct _iobuf FILE;
#define _FILE_DEFINED

每打开一个文件,系统便会根据文件的情况创建一个对应的FILE类型的数据,定义一个指针指向这个数据,便可以通过这个数据访问文件中的信息,这就是文件指针。

2、文件的打开和关闭

文件读写前需要打开,访问信息,使用结束后应该关闭,存储信息; 在打开文件的同时,函数会返回一个FILE*的指针,从而建立指针与文件的关系;

a、打开函数 fopen

image.pngfunction of open,打开功能。 fopen会在成功打开文件时,返回指向该文件信息区的指针,打开失败则会返回NULL;

  • 功能 打开文件;
  • 使用 fopen在打开失败时会返回NULL,因此在使用中应该对fopen的返回值进行判断,如下:
int main()
{
	FILE* pf;//定义文件类型指针
	pf = fopen("test.txt", "r");//以"r"模式打开文件pf
	//判断是否打开成功
	if(pf == NULL)
	{
		perror("fopen::");
		return 1;
	}
	return 0;
}

perror会打印对应的错误信息,而其中的fopen可以帮助我们定位到错误出现的位置,如下:image.png由于不存在test.txt文件,所以函数报错。

打开模式

由于打开文件的目的不同,有时需要读,有时需要写,因而打开文件也有不同的模式;

  • 只读模式 image.pngread读,打开文件读,若文件不存在或找不到,fopen调用失败;

  • 只写模式 image.pngwrite写,打开一个空文件写入,如果文件已存在,其中的内容会被销毁;  

  • 追加模式 image.pngappend追加,在文件末尾打开写入,在将新数据写入文件之前不删除 EOF 标记(End Of File,文件结束标志);若文件不存在,会创建一个新文件。  

  • 三种不同的读写模式 image.png打开文件读写(文件必须存在); image.png打开一个空文件读写,若文件存在,则其中的内容会被清除; image.png打开文件读和追加,追加操作会在将新数据写入文件之前删除 EOF 标记,并在写入完成后恢复 EOF 标记;如果文件不存在,则会创建一个文件;  

  • 打开二进制文件 只需在对应的字符后加b即可: "rb" "wb" "ab" "rb+" "wb+" "ab+",便是对应的操作。

b、关闭函数 fclose

image.png**function of close,关闭功能。**fclose会关闭文件,成功则返回0,失败则返回EOF;同时会刷新缓冲区,保存修改。

3、文件的读写

a、顺序读写

  • 字符输入函数 fgetc image.pngfunction of get a character,获取一个字符的功能。 fget格式化会从流中读取一个字符,并返回其对应的ASCII码值,将其中参数换为stdin时,fgetc会从缓冲区中获取一个字符。 例如: image.png输入abcd时,缓冲中有四个字符a、b、c、d,而调用了三次fopen,每次拿取一个,分别给到a、b、c;

  • 字符输出函数 fputc image.pngfunction of put a character,放置一个字符的功能。fputc会将一个字符放到流中,成功则返回此字符的ASCII码值,失败则返回EOF。 如输入成功时: image.png以写模式创建了test.txt文件,并以fputc往其中放入了c字符,fputc返回了c的ASCII码值,在屏幕上打印了出来; 输入失败时:image.png以"r"只读模式打开,不可写入,因此fgetc的返回值为EOF,无任何打印。 值得注意的是,若将此时代码中的pf换为stdout,则会在屏幕上打印出c字符,并成功将其赋值给a,并再次打印,从而出现两个c字符,如下: image.png

  • 文本行输入函数 fgets image.pngfunction of get a string,获取一个字符串的功能。fgets会从流中获取n-1个字符,在结尾放置 '\0' 存储在string中,并返回string的起始地址,若调取失败,则会返回NULL; 如下:失败时image.png此时test.txt中无数据,所以调取失败,a没有被修改,fgets返回值为NULL,并赋值给b; 成功时:image.png此时test.txt中数据为abcdef,调取了4-1个字符,给a,同时添加了‘\0’置于末尾,并返回地址,赋值给b; 此时a中数据为: abc\0xxxxx\0,如下, image.png

  • 文本行输出函数 fputs image.pngfunction of put a string, 放置一个字符串的功能。 fputs会将一个字符string写到流中,如果写入成功,函数会返回一个非负值,如果失败则会返回EOF; 如下:成功时,image.pngimage.png先以fopen创建了test.txt文件,后将字符串a以fputs放置到了pf指针指向的test.txt的文件中,同时将fputs的返回值赋值给了b。由输出可知,b为非负值,判断此次赋值成功,打开test.txt,验证如实:image.png 失败时, image.png以只读模式打开文件test.txt,此时test.txt中数据已被手动清除,fputs返回值赋值给b,b值为-1,由此得,此次赋值未成功image.png打开test,其中仍无数据;   值得注意的是,字符串string必须以'\0'结尾,否则fputs在复制完字符串后还会复制随机个字符,直到遇到'\0'。如下: image.pngimage.png

  • 格式化输入函数 fscanf fscanf的操作方式与scanf很相似,只是多了一个文件指针用来确定从何处获取数据。image.pngformatted scan function,格式化的扫描功能。fscanf会从流中获取数据,并返回成功分配的字段数,返回0表示未分配任何字段,若发生错误,或在未获取数据前便到达了文件流的末尾,则返回值为EOF。如下: 现在test.txt中手动输入数据,image.png再以只读模式打开test.txt,以fscanf获取其中的数据,并将返回值赋值给b;image.png可得,返回值为3,因为成功获取了3组数据; 若获取失败,返回EOF,值为-1,如下: image.png此时,test.txt中无数据,结构体a中数据仍未初始给的值,并未成功赋值。返回值为-1,因为未开始赋值便遇到了文件末尾;同样在以只读模式打开时,一样会返回-1.

  • 格式化输出函数 fprintf 同样,fprintf的功能也和printf相似,只是多了文件指针;image.pngformatted print function,格式化的打印函数。fprintf会在流中打印数据,并返回打印成功的字节数,若发生错误则返回负值。 如下,成功时:image.png字符串"zhangsan"9个字节,因为结尾有'\0',int类型的20,4个字节,double类型的80.0,8个字节,加在一起21个字节,正好是b的值。由此得以验证,打开test.txt:image.png失败时:image.png返回值为-1,表示此次打印失败,打开test.txt验证:image.png

b、随机读写

  • fseek 此函数笔者愚钝,未能想到可以自洽的方式以解释其命名。image.png将文件指针移到指定的位置; 在解释前需要首先介绍几个文件类型中定义的宏:SEEK_CUR,文件指针当前位置,SEEK_END,文件末尾,SEEK_SET,文件起始位置。 我们使用如下代码,test.txt中的数据为 abcdefg ,我们逐句代码分析,以代码注释说明:
int main()
{
	FILE* pf;//定义文件类型指针
	pf = fopen("test.txt", "r");//以"r"模式打开文件pf
	//判断是否打开成功
	if (pf == NULL)
	{
		perror("fopen::");
		return 1;
	}
	//打开文件,其文件指针当前位置应指向a
	fseek(pf, 1, SEEK_CUR);//向后偏移1位,指向b
	int x = fgetc(pf);//获取b,将其ASCII码值传给x,同时指针再次后移指向c
	printf("%c\n", x);//打印x,应为b
	fgetc(pf);//获取c,同时指针再次后移指向d
	x = fgetc(pf);//获取d,将其ASCII码值传给x,同时指针再次后移指向e
	printf("%c\n", x);//打印x,应为d
	fseek(pf, 2, SEEK_CUR);//再次往后偏移2位,应指向g
	x = fgetc(pf);//获取g,将其ASCII码值传给x,同时指针再次后移指向结束标志
	printf("%c\n", x);//打印x,应为g
	return 0;
}

image.png分析正确。

  • ftell image.png获取文件指针的位置。ftell会返回文件指针相对起始位置的偏移量,如下:image.png

  • rewind image.pngrewind会将文件指针返回起始位置,无返回值。使用如下: image.png经过此次rewind,文件指针返回起始位置,此时再往后偏移2位,便指向了c。

二、通讯录的修改

1、初始化的修改

创建contact.txt,将通讯录的信息保存在其中,同时初始化需要读取其中数据;

// 扩容
void Enlarge(Contact* con)
{
	con->capacity += 2;
	Peo* tmp = NULL;
	tmp = (Peo*)realloc(con->data, (con->capacity) * sizeof(Peo));
	if (tmp != NULL)
	{
		con->data = tmp;
		printf("扩容成功\n");
	}
	else
	{
		printf("扩容失败\n");
		con->capacity -= 2;
	}
}
//加载文件
void Load_Contact(Contact* con, FILE* pf)
{
	Peo tmp = { 0 };
	while (EOF != fscanf(pf, "%s %s %d %s %s ", tmp.name, tmp.sex, &(tmp.age), tmp.tel, tmp.addr))
	{
		if (con->sz == con->capacity)
		{
			//扩容
			Enlarge(con);
		}
		con->data[con->sz] = tmp;
		con->sz++;
	}
}
//初始化
void Init_Contact(Contact* con)
{
	assert(con);
	FILE* pf = NULL;
	pf = fopen("contact.txt", "r");
	if (pf == NULL)
	{
		perror("Init_Contact::fopen");
		return;
	}
	con->sz = 0;
	con->capacity = 3;
	con->data = (Peo*)malloc(sizeof(Peo) * con->capacity);
	Load_Contact(con, pf);
	fclose(pf);
	pf = NULL;
}

往fscanf读到文件末尾时,会返回EOF,由此判断是否完成文件的读取;

2、退出的修改

增加保存通讯录函数:

void Save_Contact(Contact* con)
{
	FILE* pf;
	pf = fopen("contact.txt", "w");
	if (pf == NULL)
	{
		perror("Save_Contact::fopen::");
		return;
	}
	int i = 0;
	for (i = 0; i < con->sz; i++)
	{
		fprintf(pf, "%s %s %d %s %s ", (con->data + i )->name, (con->data + i)->sex, (con->data + i)->age, (con->data + i)->tel, (con->data + i) ->addr);
	}
	fclose(pf);
	pf = NULL;
}

增加文件的修改和保存,便对通讯录的加载提供了有效的效率提升; 文件代码已上传到Gitee——>通讯录代码,大家如果有需要可以前往查看,如果文章对您有帮助,希望可以得到您的一个赞,*^____^*。

标签:文件,指针,详解,pf,通讯录,test,txt,con
From: https://blog.51cto.com/u_15423682/6142178

相关文章

  • container和主机(host)之间的文件拷贝
    [b]1.从container到主机(host)[/b]使用dockercp命令dockercp<containerId>:/file/path/within/container/host/path/target比如安装mysql之后,复制的到my.cnfdock......
  • Linux如何查找大文件或目录总结
    [size=large][color=red]如何查找大文件[/color][/size]1.要搜索当前目录下,超过800M大小的文件find.-typef-size+800M2.对文件的信息(例如,文件大小、文件属性)一无......
  • Java关键字-volatile详解
    点击  ​​Mr.绵羊的知识星球​​ 解锁更多优质文章。一、介绍1.简介  volatile是java关键字,同时也是JVM提供的轻量级的同步机制2.特性  你需要先了解一下Jav......
  • Html5添加移动触摸的网页版PDF格式文件阅读器插件
    一、使用方法<scripttype="text/javascript"src="jquery.min.js"></script><scripttype="text/javascript"src="pdf.compatibility.js"></script><scripttype="text/......
  • SpringMVC配置文件位置
    [size=large][color=red]在工程内:[/color][/size]<context:property-placeholderlocation="classpath:/site.properties"/>[size=large][color=red......
  • Windows上使用bat实现备份一个月内的数据库数据到文件
    场景上面是通过bat和mysqldump将数据库导出到文件。但是只能导出一个最新的文件。如果想要将数据库一天导出备份一次,然后保留近一个月的数据库版本备份文件。注:关注公众号......
  • 处理URL重写后postback重写失效的问题 .browser文件
     大家在使用ASP.NET和重写URL时经常遇到的一个疑难杂症跟处理postback场景有关,具体地来说,当你在一个网页上放置一个<formrunat="server">控件时,ASP.NET会自动地默认输......
  • linux 複製大文件
    Linux如果使用cp命令複製大文件,會很慢,複製幾個G都要好久,這時候需要用另外一種工具rsync去複製;rsync是linux系统下的数据镜像备份工具。使用快速增量备份工具Remote......
  • Django中多个app放置同一文件夹中
    在pycharm中新建一个管理app的pythonpackage目录:apps将存在的app用拖拽到apps目录下,此时会弹出对话框,取消勾选Searchforreferences(搜索索引)和openmovedfilesine......
  • 读取文件的三个方法
    packagemainimport( "bufio" "fmt" "io" "io/ioutil" "os")funcdome01(){ file,err:=os.Open("E:\\GOGO\\GOE\\dome01\\main.go") iferr!=nil{ ......