C语言文件操作

 

目录

   前言

初识文件

 文件的打开和关闭

文件的顺序读写

函数对比

文件的随机读写:

文件读取结束的判定


   前言

      文件这个名词就好像是一个熟悉的“陌生人”,它好像经常出现在我们的生活中,但又没有真正的了解过。例如:密封档案中的绝密文件,个人信息的复印文件,计算机磁盘上的文件等等。

      我们今天要谈论的是在C语言程序设计中关于文件的操作,在程序设计当中,我们口中的文件一般有两种:程序文件和数据文件。

程序运行,如果需要从文件中读取数据, 或者输出数据时。这些内容就存储在数据文件中。数据文件是本次学习的内容。

初识文件

文件名:文件名是一个文件的唯一标识。

文件名的组成:文件路径+文件名主干+文件后缀。

例如:D:\code\gitee\test.txt。

D:\code\gitee\是文件路径,test是文件名主干,.txt是文件后缀。

既然说到了文件路径,那么接下来就要分清楚相对路径和绝对路径两兄弟。

相对路径:相对于当前文件下的路径。

绝对路径:完整的描述文件位置的路径,从根目录开始,文件在磁盘上的路径。

 文件的打开和关闭

每当我们需要完成一件事情,都需要一定的步骤。例如:把大象关进冰箱,首先要打开冰箱门,其次要把大象装进冰箱,最后关上冰箱门。

 文件操作也是一样的道理,无论我们写入或者读取什么样的数据,打开和关闭文件都是不可或缺的重要步骤。

当打开一个文件时,系统会根据文件的情况自动创建一个 FILE结构的变量,并填充其中的信息, 当然在这期间发生的事情我们不必关心。我们只需要知道,通过一个 FILE类型 的指针就可以维护这个 FILE 结构的变量。
FILE* pf;//文件指针变量

当定义好文件指针变量后,就可以通过文件指针变量找到与它关联 的文件。 了解这些后,接下来学习使用两个函数,ANSI C(由美国国家标准协会(ANSI)及国际标准化组织(ISO)推出的关于C语言的标准​) 规定使用fopen函数来打开文件,fclose函数来关闭文件。

fopen:

返回值:打开文件成功则返回一个指向FILE对象的指针,否则返回空指针。类似于使用动态内存开辟函数,当用fopen函数打开文件时,应该判断是否返回NULL。

参数:要打开的文件;文件的使用方式

头文件:#include <stdio.h>


flose: 

参数:指向FILE对象的指针;

返回值:如果关闭成功返回0,返回失败时返回EOF。

头文件:#include <stdio.h>


使用这两个函数,看下效果:

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("text.txt","w");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	//文件操作
	

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

找到这个程序的路径

运行程序后:

 通过对比不难发现,程序运行后,因为要写的文件不存在,所以建立了一个新的text.txt的文本文件。

文件的顺序读写

刚刚在上面谈到过,文件操作的第2步就是对文件进行一些读、写等操作。在学习文件操作前处理数据的输入输出都是以终端为对象的,从终端的键盘输入数据,运行结果显示到显

示器上。接下来学习一些函数对文件进行一系列操作,通过这些函数可以把数据在数据文件中操作。

 fputc:

功能:将内容写入文件中

参数:要写入的内容,指向FILE对象的指针。

应用:把26个字母写入文件中

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("text.txt","w");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	//文件操作
	
	for (int i = 0; i < 26; i++)
	{
	
		fputc('a' + i, pf);
	}

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


fgetc: 

 功能:从指定文件中读取数据

参数:指向FILE对象的指针

练习:读取指定文件中的内容,因为我知道文件的内容,所以使用了for循环,也可使用while循环。重点在于练习fgetc函数的使用。

int main()
{
	//打开文件
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	//文件操作

	for (int i = 0; i < 26; i++)
	{

		int ch = fgetc(pf);
		printf("%c ", ch);
	}

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

fgetc和fputc:

#include <stdio.h>
int main()
{
	int ch = fgetc(stdin);
	fputc(ch, stdout);

	return 0;
}

fputs:

功能:写入字符串

 参数:写入str指向的字符串,指向FILE对象的指针

int main()
{
	//打开文件
	FILE* pf = fopen("text.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	//文件操作
	fputs("Hello ",pf);
	fputs("Word!",pf);

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

fgets:

 功能:从指定文件中读取字符串

参数:读取的字符串存储到str中,读取 (num-1) 个字符或到达换行符或文件结尾,指定的FILE对象。

int main()
{
	//打开文件
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	//文件操作
	char str[20] = { 0 };
	fgets(str,15,pf);
	printf("%s",str);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}


fwrite

功能 :以二进制的方式把数据写入文件

参数:指向(要写入元素数组)的指针,元素的大小,元素的个数,指向FILE对象的指针

struct str
{
	char name[20];
	int age;
	char addr[20];
};

int main()
{
	//打开文件
	FILE* pf = fopen("text.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	//文件操作
	//以2进制的方式写入文件
	struct str s = {"张西洋",18,"内蒙"};
	fwrite(&s,sizeof(s),1,pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

 通过观察发现,以二进制的方式写入,打开这个文本文件时,部分数据好像是这个文件和计算机间的加密暗语,我们无法从中读取有效信息。

fread:

 功能:以二进制的方式读取文件中的数据

参数:指向一块空间的指针,元素大小,元素个数,指向FILE对象的指针

struct str
{
	char name[20];
	int age;
	char addr[20];
};

int main()
{
	//打开文件
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	//文件操作
	//以2进制的方式读取数据
	struct str s = { "张西洋",18,"内蒙" };
	fread(&s, sizeof(s), 1, pf);
	printf("%s %d %s\n", s.name, s.age, s.addr);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}


学习了这几个函数,回想下之前写过的通讯录,每次退出程序,联系人并没有保存,这显然是不符合我们的需求,那么可不可以运用本篇文章的知识来使这些数据保存起来呢?

这里只写下保存数据和读取数据的功能:

按照通讯录的结构,保存联系人应该是在退出通讯录时发生。读取联系人应该是初始化通讯录时,把信息加载到通讯录中。

1.退出时保存通讯录

 

//保存数据
void SaveContact(struct Contact* pc)
{
    //打开文件
    //wb读和写
    FILE* pw = fopen("Datd.txt","wb");
    if (pw == NULL)
    {
        perror("fopen");
        return;
    }

    //写入数据
    for (int i = 0; i < pc->sz; i++)
    {
        fwrite(pc->data+i,sizeof(struct PeoInfo),1,pw);
    }
    //关闭文件
    fclose(pw);
    pw = NULL;
}

保存数据成功

 重新运行通讯录:通讯录没有数据

 

2.在次打开通讯录可以载入保存的数据,这个动作应该发生在初始化通讯录的过程中。

static int check_capacity(struct Contact* pc)
{
	//联系人的存储数量和容量相等
	if (pc->sz == pc->capacity)
	{
		//增容
		struct PeoInfo* ptr = (struct PeoInfo*)realloc(pc->data, pc->capacity + increase_sz * sizeof(struct PeoInfo));
		//判断
		if (ptr == NULL)
		{
			perror("check_capacity");
			return 0;
		}
		else
		{
			//增容成功
			pc->data = ptr;
			pc->capacity = pc->capacity + increase_sz;
			printf("增容成功!\n");
			return 1;
		}
	}
	else
	{
		return 1;
	}

}
void LoadContact(struct Contact* pc)
{
	//打开文件
	FILE* pr = fopen("Datd.txt","rb");
	if (pr == NULL)
	{
		perror("LoadContact>fopen");
		return;
	}
	//读取文件
	struct PeoInfo tmp = {0};
	while (fread(&tmp, sizeof(struct PeoInfo), 1, pr))
	{
		//判断是否需要增容
		check_capacity(pc);
		//载入数据
		pc->data[pc->sz] = tmp;
		pc->sz++;
	}

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

//通讯录初始化
void InitContact(struct Contact* pc)
{
	//断言
	assert(pc != NULL);
	//动态内存开辟
	pc->data = (struct PeoInfo*)malloc(capacity_max*sizeof(struct PeoInfo));
	//判断
	if (pc->data == NULL)
	{
		perror("InitContact");
		return;
	}
	pc->capacity = capacity_max;
	pc->sz = 0;

	//加载文件中的数据到通讯录中
	LoadContact(pc);
}

在次打开通讯录,之前保存的数据还在,载入成功。

 这样就实现了数据的保存,你学“废”了吗?具体关于通讯录实现的代码在之前的文章中,有兴趣的小伙伴可以去了解一下。

函数对比

scanf/fscanf/sscanf
printf/fprintf/sprintf
功能:按照一定的格式从键盘输入数据。

 功能:按照一定的格式把数据输出到屏幕上。

总结:适用于标准输入/输出流的格式化输入输出数据。

功能:按照一定的格式向输出流(文件/stdout)输出数据。 



struct S
{
	char name[20];
	int age;
	float score;
};

int main()
{
	struct S s = { "张三", 20, 88.5f };
	//把s中的数据写到文件中
	FILE*pf = fopen("test.txt", "w");
	if (NULL == pf)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fprintf(pf, "%s %d %.1f", s.name, s.age, s.score);

	fclose(pf);
	pf = NULL;
	return 0;
}

 

功能:按照一定格式从输入流(文件/stdin)输入数据。

读取一个结构体数据
struct S
{
	char name[20];
	int age;
	float score;
};

int main()
{
	struct S s = {0};
	//把s中的数据写到文件中
	FILE* pf = fopen("test.txt", "r");
	if (NULL == pf)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));


	printf("%s %d %f\n", s.name, s.age, s.score);

	fclose(pf);
	pf = NULL;
	return 0;
}

 

总结:适用于所有输入/输出流的格式化输入输出数据。

对比:

//scanf(...)
//fscanf(stdin,...)

//printf
//fprintf(stdout, ...)

功能:把格式化的数据按照一定的格式转化成字符串 。

参数:指向存储生成的 C 字符串的缓冲区的指针,一定的格式

功能:从字符串中按照一定的格式读取出格式化的数据。

参数: 字符串,一定的格式。

struct S
{
	char name[10];
	int age;
	float score;
};

int main()
{
	char buf[100] = {0};
	struct S tmp = { 0 };

	struct S s = { "lisi", 20, 95.5f };
	//把结构体的数据,转换成字符串
	sprintf(buf, "%s %d %f", s.name, s.age, s.score);
	//以字符串的形式打印
	printf("%s\n", buf);
	//printf("%s %d %f\n", s.name, s.age, s.score);
	//将buf中的字符串,还原成一个结构体数据
	sscanf(buf, "%s %d %f", tmp.name, &(tmp.age), &(tmp.score));
	//以结构的形式打印
	printf("%s %d %f\n", tmp.name, tmp.age, tmp.score);

	return 0;
}

文件的随机读写:

fseek:

功能:根据文件指针的位置和偏移量来定位文件指针。

参数:指向FILE的指针;二进制文件:要从原点偏移的字节数。文本文件:零,或 ftell 返回的值;用作偏移参考的位置

 

int main()
{
	FILE* pf;
	pf = fopen("text.txt", "wb");
	fputs("Hello Word", pf);
	fseek(pf, 9, SEEK_SET);
	fputs(" ***", pf);
	fclose(pf);
	return 0;
}

 

 

ftell:

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

参数:指向FILE对象的指针

返回值:成功时,返回文件指针相对于起始位置的偏移量。失败时,返回 -1L,并将 errno 设置为系统特定的正值。

int main()
{
	FILE* pFile;
	long size = 0;
	pFile = fopen("text.txt", "rb");
	if (pFile == NULL)
	{
		perror("fopen");
	}
	
		fseek(pFile, 0, SEEK_END);
		size = ftell(pFile);
		//关闭文件
		fclose(pFile);
		printf("Size of text.txt: %ld bytes.\n", size);
	return 0;
}

 

rewind:

功能:让文件指针的位置回到文件的起始位置

参数:指向FILE对象的指针

int main()
{
	int n;
	FILE* pf;
	char buffer[27];
	pf = fopen("text.txt", "w+");
	for (n = 'A'; n <= 'Z'; n++)
	{
		fputc(n, pf);
	}
	//rewind(pf);
	//二进制输入
	fread(buffer, 1, 26, pf);
	fclose(pf);

	buffer[26] = '\0';
	printf("%s",buffer);
	return 0;
}

未使用rewind函数。

使用rewind函数让文件指针的位置回到文件的起始位置。

 

 

文件读取结束的判定

feof:

功能:当文件读取结束时,判断是读取失败结束,还是遇到文件尾结束。
参数:指向FILE对象的指针。
1. 文本文件读取是否结束,判断返回值是否为 EOF fgetc ),或者 NULL fgets )。
例如:
fgetc 判断是否为 EOF .
fgets 判断返回值是否为 NULL .
2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如:fread判断返回值是否小于实际要读的个数。

今天的分享就到这里,继续加油!

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值