0
点赞
收藏
分享

微信扫一扫

文件操作.

hwwjian 2022-02-24 阅读 45
gnu服务器

一.文件

文件有两种:程序文件、数据文件(从文件功能的角度来分类的)

程序文件:

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

后缀为 .exe

数据文件:

文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,

或者输出内容的文件

这篇文章主要讨论的是数据文件

文件名:

文件标识常被称为文件名,以便用户识别和引用。

文件名包含 3 部分:文件路径 + 文件名主干 + 文件后缀

例如: D :\666\code\test.txt


二、文件的打开和关闭

文件指针打开与关闭

文件指针变量能够找到与它关联 的文件

文件指针变量创建方式:

FILE* pf;

ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件

1.    fopen

FILE * fopen ( const char * filename, const char * mode );

2.    fclose

int fclose ( FILE * stream );

打开方式:

文件使用方式

含义

如果指定文件不存在

“r” (只读)

为了输入数据,打开一个已经存在的文本文件

出错

“w” (只写)

为了输出数据,打开一个文本文件

建立一个新的文件

“a” (追加)

向文本文件尾添加数据

建立一个新的文件

“rb” (只读)

为了输入数据,打开一个二进制文件

出错

“wb” (只写)

为了输出数据,打开一个二进制文件

建立一个新的文件

“ab” (追加)

向一个二进制文件尾添加数据

出错

“r+” (读写)

为了读和写,打开一个文本文件

出错

“w+” (读写)

为了读和写,建议一个新的文件

建立一个新的文件

“a+” (读写)

打开一个文件,在文件尾进行读写

建立一个新的文件

“rb+” (读写)

为了读和写打开一个二进制文件

出错

“wb+” (读写)

为了读和写,新建一个新的二进制文件

建立一个新的文件

“ab+” (读写)

打开一个二进制文件,在文件尾进行读和写

建立一个新的文件

使用:

int main()
{
	//创建文件指针变量
	//打开文件
	FILE* pf = fopen("test.txt", "w");//如果不加文件路径,则默认将test.txt写到与该代码相同地址
	if (pf == NULL);
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

三、文件的顺序读取

功能

函数名

适用于

字符输入函数

fgetc

所有输入流

字符输出函数

fputc

所有输出流

文本行输入函数

fgets

所有输入流

文本行输出函数

fputs

所有输出流

格式化输入函数

fscanf

所有输入流

格式化输出函数

fprintf

所有输出流

二进制输入

fread

文件

二进制输出

fwrite

文件

fputc   ->   int fputc( int c, FILE *stream );

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//写文件
	char ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		fputc(ch, pf);//一个一个将a - z写入test.txt
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

fgetc   ->   int fgetc( FILE *stream );

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");//只读文件
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//读文件
	int ch = 0;
	while ((ch = fgetc(pf)) != EOF)//文件无数据输入时,fgetc返回EOF
	{
		printf("%c ", ch);//一个一个读取文件指针pf指向文件中的数据
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

fputs   ->   int fputs( const char *string, FILE *stream );

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//写文件
	fputs("hello world\n", pf);//将字符串写入test.txt中
	fputs("hahahahahaha\n", pf);//
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

fgets    ->    char *fgets( char *string, int n, FILE *stream );

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//读文件
	char buf[100] = { 0 };
	fgets(buf, 5, pf);
	printf("%s", buf);
	fgets(buf, 5, pf);//接着上面的地方往后读取5个
	printf("%s", buf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

用代码实现将文件的内容拷贝一份:

int main()
{
	//创建文件指针变量
	//打开文件
	FILE* pf = fopen("test.txt", "r");//只读
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	FILE* pf2 = fopen("test2.txt", "w");//只写
	if (pf2 == NULL)
	{
		printf("%s\n", strerror(errno));
		fclose(pf);
		pf = NULL;
		return 0;
	}
	//拷贝文件test.txt内容到test2.txt中
	int ch = 0;
	while ((ch = fgetc(pf)) != EOF)//从pf从一个个读取
	{
		fputc(ch, pf2);//一个个输出至pf2中
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	fclose(pf2);
	pf2 = NULL;
	return 0;
}

fprintf   ->   int fprintf( FILE *stream, const char *format [, argument ]...);

struct stu
{
	char name[20];
	int age;
	char sex[5];
};
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//写格式化的数据
	struct stu N[2] = { {"张三",18,"男"},{"李四",19,"男"}};
	int i = 0;
	for (i = 0; i < 2; i++)
	{
		fprintf(pf, "%s  %d  %s\n", N[i].name, N[i].age, N[i].sex);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

fscanf   ->   int fscanf( FILE *stream, const char *format [, argument ]... );

struct stu
{
	char name[20];
	int age;
	char sex[5];
};
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//读格式化的数据
	struct stu N[2] = { 0 };
	int i = 0;
	for (i = 0; i < 2; i++)
	{
		fscanf(pf, "%s  %d  %s\n", N[i].name, &N[i].age, N[i].sex);
		printf("%s  %d  %s\n", N[i].name, N[i].age, N[i].sex);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

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

以二进制的方式写入

struct stu
{
	char name[20];
	int age;
	char sex[5];
};
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "wb");//wb , 为了输出数据,打开一个二进制文件
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//二进制的写入
	struct stu N[2] = { {"张三",18,"男"},{"李四",19,"男"}};
	fwrite(N,sizeof(struct stu),2,pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

fread   ->   size_t fread( void *buffer, size_t size, size_t count, FILE *stream );

以二进制的方式读取

struct stu
{
	char name[20];
	int age;
	char sex[5];
};
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "rb");//rb , 为了输入数据,打开一个二进制文件
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//二进制的读取
	struct stu N[2] = { 0 };
	fread(N,sizeof(struct stu),2,pf);
	int i = 0;
	for (i = 0; i < 2; i++)
	{
		printf("%s  %d  %s\n", N[i].name, N[i].age, N[i].sex);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

sscanf和sprintf

struct stu
{
	char name[20];
	int age;
	char sex[5];
};
int main()
{
	struct stu S = { "张三",18,"男" };
	struct stu tmp = { 0 };
	char buf[100] = { 0 };
	sprintf(buf, "%s ,%d ,%s", S.name, S.age, S.sex);//将S中的格式化数据转换为字符串存入buf
	printf("%s\n", buf);

	sscanf(buf, "%s ,%d ,%s", tmp.name, &tmp.age, tmp.sex);//将buf中的字符串提取出格式化数据存入tmp中
	printf("%s ,%d ,%s\n", tmp.name, tmp.age, tmp.sex);
	return 0;
}

对比以下一组函数

scanf      从标准输入流(stdin)上进行格式化输入的函数

printf      向标准输出流(stdout)上进行格式化输出的函数

fscanf    可以从标准输入流(stdin)/指定的文件流 上读取格式化数据

fprintf    把数据按照格式化的方式输出到标准输出流(stdout)/指定的文件流

sscanf   可以从一个字符串中提取(转化)出格式化数据

sprintf   把一个格式化的数据转换为字符串

结合文件知识改良通讯录

将信息保存在文件中

test.c

#include "contact.h"
void menu()
{
	printf("*************************************\n");
	printf("*****    1.add        2.del     *****\n");
	printf("*****    3.search     4.modify  *****\n");
	printf("*****    5.sort       6.show    *****\n");
	printf("*****    0.exit                 *****\n");
	printf("*************************************\n");
}
int main()
{
	int input = 0;
	conact con;
	Initconact(&con);
	do {
		menu();
		printf("请选择要进行的操作:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			Addconact(&con);
			break;
		case 2:
			Delconact(&con);
			break;
		case 3:
		{
			char name[10] = "";
			printf("请输入需要查询的名字:");
			scanf("%s", name);
			int n = Findconact(&con, name);
			if (n != -1)
			{
				printf("% -10s\t% -5s\t% -5s\t% -15s\t% -20s\n", "名字", "性别", "年龄", "电话", "地址");
				printf("%-10s\t%-5s\t%-5d\t%-15s\t%-20s\n",
					con.date[n].name, con.date[n].sex, con.date[n].age,
					con.date[n].tele, con.date[n].addr);
			}
			else
			{
				printf("找不到此联系人\n");
			}
			break;
		}
		case 4:
			Changeconact(&con);
			break;
		case 5:
			Sortconact(&con);
			break;
		case 6:
			Showconact(&con);
			break;
		case 0:
			Destroyconact(&con);
			Saveconact(&con);
			printf("退出通讯录\n");
			break;
		default:
			printf("输入错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

contact.c

#include "contact.h"
void check(conact* pc)
{
	assert(pc);
	if (pc->sz == pc->capacity)
	{
		//增加容量
		PeoInfo* p = (PeoInfo*)realloc(pc->date, (pc->capacity + 2) * sizeof(PeoInfo));
		if (p != NULL)
		{
			pc->date = p;
			pc->capacity += 2;
			printf("增容成功\n");
		}
		else
		{
			printf("check()::%s\n", strerror(errno));
		}
	}
}

void Loaddownconact(conact* pc)
{
	FILE* pf = fopen("conact.txt", "rb");
	if (pf == NULL)
	{
		printf("Initconact:open for reading :()%s\n", strerror(errno));
		return;
	}
	PeoInfo buf = { 0 };
	while (fread(&buf, sizeof(PeoInfo), 1, pf))
	{
		check(pc);
		pc->date[pc->sz] = buf;
		pc->sz++;
	}
	fclose(pf);
	pf = NULL;
}
void Saveconact(conact* pc)
{
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL)
	{
		printf("Saveconact::()%s\n", strerror(errno));
		return;
	}
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		fwrite(pc->date + i, sizeof(PeoInfo), 1, pf);
	}
	fclose(pf);
	pf = NULL;
}


void Destroyconact(conact* pc)
{
	assert(pc);
	free(pc->date);
	pc->date = NULL;
	pc->sz = 0;
	pc->capacity;
}

void Initconact(conact* pc)
{
	assert(pc);
	pc->sz = 0;
	PeoInfo* p = (PeoInfo*)malloc(Default_Max * sizeof(PeoInfo));
	if (p != NULL)
	{
		pc->date = p;
	}
	else
	{
		printf("Initconact()::%s\n", strerror(errno));
		return;
	}
	pc->capacity = Default_Max;
}



void Addconact(conact* pc)
{
	assert(pc);
	check(pc);
	printf("请输入名字:");
	scanf("%s", pc->date[pc->sz].name);
	printf("请输入性别:");
	scanf("%s", pc->date[pc->sz].sex);
	printf("请输入年龄:");
	scanf("%d", &pc->date[pc->sz].age);
	printf("请输入电话:");
	scanf("%s", pc->date[pc->sz].tele);
	printf("请输入地址:");
	scanf("%s", pc->date[pc->sz].addr);

	pc->sz++;
	printf("添加联系人成功\n");
}

void Showconact(const conact* pc)
{
	assert(pc);
	printf("%-10s\t%-5s\t%-5s\t%-15s\t%-20s\n", "名字", "性别", "年龄", "电话", "地址");
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-10s\t%-5s\t%-5d\t%-15s\t%-20s\n",
			pc->date[i].name, pc->date[i].sex, pc->date[i].age,
			pc->date[i].tele, pc->date[i].addr);
	}
}

int Findconact(const conact* pc, char name[])
{
	assert(pc);
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->date[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}

void Changeconact(conact* pc)
{
	printf("请输入需要修改的联系人:");
	char name[20] = "";
	scanf("%s", name);
	int n = Findconact(pc, name);
	if (n != -1)
	{
		Showconact(pc);
		printf("请输入修改后的名字:");
		scanf("%s", pc->date[n].name);
		printf("请输入修改后的性别:");
		scanf("%s", pc->date[n].sex);
		printf("请输入修改后的年龄:");
		scanf("%d", &pc->date[n].age);
		printf("请输入修改后的电话:");
		scanf("%s", pc->date[n].tele);
		printf("请输入修改后的地址:");
		scanf("%s", pc->date[n].addr);
		printf("修改成功\n");
	}
	else
	{
		printf("找不到该联系人\n");
	}
}

void Delconact(conact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空\n");
		return;
	}
	printf("请输入要删除的联系人:");
	char name[20] = "";
	int i = 0;
	scanf("%s", name);
	int n = Findconact(pc, name);
	if (n != -1)
	{
		for (i = n; i < pc->sz - 1; i++)
		{
			pc->date[i] = pc->date[i + 1];
		}
		pc->sz--;
		printf("删除成功\n");
	}
	else
	{
		printf("找不到联系人\n");
		return;
	}
}

void Sortconact(conact* pc)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < pc->sz - 1; i++)
	{
		for (j = 0; j < pc->sz - 1 - i; j++)
		{
			if (strcmp(pc->date[j].name, pc->date[j + 1].name) > 0)
			{
				char t[Name_Max] = "";
				strcpy(t, pc->date[j].name);
				strcpy(pc->date[j].name, pc->date[j + 1].name);
				strcpy(pc->date[j + 1].name, t);
			}
		}
	}
	printf("排序完成\n");
}

contact.h

#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define Max 1000
#define Name_Max 20
#define Sex_Max 5
#define Addr_Max 30
#define Tele_Max 12
#define Default_Max 3
typedef struct PeoInfo
{
	char name[Name_Max];
	char sex[Sex_Max];
	char addr[Addr_Max];
	char tele[Tele_Max];
	int age;
}PeoInfo;
//通讯录的结构体
typedef struct conact
{
	//PeoInfo data[MAX];
	PeoInfo* date;//存放数据
	int sz;//通讯录中有效信息个数
	int capacity;//记录当前通讯录的最大容量
}conact;

void Initconact(conact* pc);

void Addconact(conact* pc);

void Showconact(const conact* pc);

void Delconact(conact* pc);

int Findconact(const conact* pc, char name[]);

void Changeconact(conact* pc);

void Sortconact(conact* pc);

void Destroyconact(conact* pc);

void Saveconact(conact* pc);

void Loaddownconact(conact* pc);

四、文件的随机读写

fseek

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

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

SEEK_CUR   从当前位置

SEEK_END   从结尾位置

SEEK_SET    从起始位置

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//test.txt里存有  abcdefghijklmnopqrstuvwxyz
	char ch = 0;
	ch = fgetc(pf);//a
	ch = fgetc(pf);//b
	ch = fgetc(pf);//c
	ch = fgetc(pf);//d
	fseek(pf, 2, SEEK_CUR);//SEEK_CUR  令文件指针在当前位置往后跳过两个位置, e f
	ch = fgetc(pf);//g
	ch = fgetc(pf);//h
	fseek(pf, 2, SEEK_SET);//SEEK_SET  令文件指针从第一位往后跳过两个位置,  a b
	ch = fgetc(pf);//c
	fseek(pf, -2, SEEK_END);//SEEK_END  令文件指针从最后一位往前数的第两个位置,  z y,上面都是跳,只有这个是数的,而且只能填负数
	ch = fgetc(pf);//y
	fclose(pf);
	pf = NULL;
	return 0;
}

ftell

long int ftell ( FILE * stream );

返回一个数值,即文件指针相对于起始位置的偏移量

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//test.txt里存有  abcdefghijklmnopqrstuvwxyz
	char ch = 0;
	int ret = 0;
	ch = fgetc(pf);//a
	ch = fgetc(pf);//b
	ch = fgetc(pf);//c
	ch = fgetc(pf);//d
	ret = ftell(pf);
	printf("%d\n", ret);//ret = 4,相对于起始位置,偏移量为4   a - d
	fclose(pf);
	pf = NULL;
	return 0;
}

rewind

void rewind ( FILE * stream );

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

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//test.txt里存有  abcdefghijklmnopqrstuvwxyz
	char ch = 0;
	ch = fgetc(pf);//a
	ch = fgetc(pf);//b
	ch = fgetc(pf);//c
	ch = fgetc(pf);//d
	rewind(pf);//令文件指针指向起始位置a
	ch = fgetc(pf);//a
	fclose(pf);
	pf = NULL;
	return 0;
}

五、文件读取结束的判定

1. 文本文件读取是否结束,判断返回值是否为 EOF fgetc ),或者 NULL fgets

例如:

fgetc 判断是否为 EOF .

fgets 判断返回值是否为 NULL .

2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。

例如:

fread判断返回值是否小于实际要读的个数。

feof的正确用法

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

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//test.txt里存有  abcdefghijklmnopqrstuvwxyz
	char ch = 0;
	while ((ch = fgetc(pf)) != EOF)
	{
		putchar(ch);
	}
	if (ferror(pf))
	{
		printf("文件读取失败\n");
	}
	else if(feof(pf))
	{
		printf("遇到文件尾结束\n");
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

文件缓存区

总结:因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。如果不做,可能导致读写文件的问题。


举报

相关推荐

0 条评论