C语言进阶--文件操作(1)

目录

为什么使用文件

什么是文件 

程序文件 

数据文件

文件名

文件的打开和关闭

文件指针

文件的打开和关闭

fopen和fclose函数 定义

 使用:

文件的顺序读写 

 演示fputc的使用:

fgetc的使用:

fputs的使用: 

fgets的使用:

fprintf的使用: 

 fscanf的使用:

fwrite的使用: 

fread的使用: 

 标准输入流(stdin)与标准输出流(stdout):

        

 

scanf/fscanf/sscanf

printf/fprintf/sprintf

函数sscanf和sprintf

  六个函数的对比:


为什么使用文件

例如我们写一个通讯录的时候,当通讯录运行起来的时候,可以给通讯录中增加、删除数 据,此时数据是存放在内存中,当程序退出的时候,通讯录中的数据自然就不存在了,等下次运行通讯 录程序的时候,数据又得重新录入,如果使用这样的通讯录就很难受。 我们在想既然是通讯录就应该把信息记录下来,只有我们自己选择删除数据的时候,数据才不复存在。 这就涉及到了数据持久化的问题,我们一般数据持久化的方法有,把数据存放在磁盘文件、存放到数据 库等方式。 使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化


什么是文件 

磁盘上的文件是文件。

但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。

程序文件 

包括源程序文件(后缀为.c)

目标文件(windows环境后缀为.obj)

可执行程序(windows环境 后缀为.exe)。

数据文件

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

在学习文件操作之前我们处理数据的输入输出基本都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显 示器上。其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上的文件。

文件名

一个文件要有一个唯一的文件标识,以便用户识别和引用。

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

例如: c:\exercise\text.c

为了方便起见,文件标识常被称为文件名。


文件的打开和关闭

文件指针

缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。

每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字文件状态及文件当前的位置等)。

这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE.

在库函数中,FILE通常是通过typedef定义的结构体指针类型,其声明类似于: 

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

其中:

_ptr和_cnt用于实现缓冲

_base指向缓冲区的起始地址

_flag保存文件的状态信息

_file保存操作系统中的文件描述符

_charbuf和_bufsiz用于实现字符缓冲

_tmpfname保存临时文件名

通过这些字段,可以实现对文件的读写操作。 

不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。 每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息, 使用者不必关心细节。 一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。

FILE* pf;//文件指针变量

定义pf是一个指向FILE类型数据的指针变量。

可以使pf指向某个文件的文件信息区(是一个结构体变 量)。

通过该文件信息区中的信息就能够访问该文件。

也就是说,通过文件指针变量能够找到与它关联 的文件。

如下: 

文件的打开和关闭

文件在读写之前应该先打开文件,使用结束后应当关闭文件

fopen和fclose函数 定义

//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream );
文件使用方式                             含义如果指定文件不存在
“r”(只读)为了输入数据,打开一个已经存在的文本文件出错
“w”(只写)为了输出数据,打开一个文本文件建立一个新的文件
“a”(追加)向文本文件尾添加数据建立一个新的文件
“rb”(只读)为了输入数据,打开一个二进制文件出错
“wb”(只写)为了输出数据,打开一个二进制文件        建立一个新的文件
“ab”(追加)向一个二进制文件尾添加数据出错
“r+”(读写)为了读和写,打开一个文本文件出错
“w+”(读写)为了读和写,建议一个新的文件建立一个新的文件
“a+”(读写)打开一个文件,在文件尾进行读写建立一个新的文件
“rb+”(读写)为了读和写打开一个二进制文件出错
wb+”(读写)为了读和写,新建一个新的二进制文件建立一个新的文件
“ab+”(读写)打开一个二进制文件,在文件尾进行读和写建立一个新的文件

 使用:

#include<stdio.h>
int main()
{
	//打开文件
    //相对路径
	FILE* pf = fopen("test.txt", "w");
    //绝对路径
	//FILE* pf = fopen("D:\\远程仓库\\exercise\\TEXT_3_30\\test.txt", "w");

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

这是刚刚创建的文件 :

 如果文件是存在的,那么会把原文件的内容彻底销毁,拷贝一个空文件,演示如下:

这是打开文件前:

打开文件后 : 

 

fopen使用"r"失败后会返回空指针 :

 其它的使用文件使用方式就不一一举例了,大家可以自己敲一敲,试一试


文件的顺序读写 

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

以下的函数均已经添加了超链接,若想更深入的解可以点击查看!!! 

 演示fputc的使用:

#include<stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w"); 

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	//将26个字母放入文件
	int i = 0;
	for (i = 0;i < 26;i++)
	{
		fputc('a'+i, pf);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

 效果如下:

fgetc的使用:

#include<stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r"); 

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}		
	//读文件
	int i = 0;
	for (i = 0;i < 26;i++)
	{
		int ch = fgetc(pf);
		printf("%c", ch);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

运行效果:

有同学可能会问,为什么打印的不是26个a,而是a到z ,其实是因为我们每使用一次fgetc,最初指向a的指针都会向后移一位

fputs的使用: 

#include<stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputs("hello world", pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

效果如下:

fgets的使用:

#include<stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	//要创建一个数组来接收
	char arr[20];
	fgets(arr,12,pf);//如果大于12也没事,原本有多少打印多少
	printf("%s", arr);
	fclose(pf);
	pf = NULL;
	return 0;
}

效果如下:

fprintf的使用: 

#include<stdio.h>
struct S
{
	int a;
	float b;
	char arr[20];
};
int main()
{
	struct S s = { 66,66.34f,"hello" };
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror(pf);
		return 1;
	}
	//写文件
	fprintf(pf, "%d %.2f %s", s.a, s.b, s.arr);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

效果如下:

 

 fscanf的使用:

#include<stdio.h>
struct S
{
	int a;
	float b;
	char arr[20];
};
int main()
{
	struct S s = {0};
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror(pf);
		return 1;
	}
	//读文件
	fscanf(pf, "%d %f %s", &(s.a), &(s.b), s.arr);
	printf("%d %.2f %s", s.a,s.b,s.arr);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

效果如下:

fwrite的使用: 

//二进制的写文件
#include<stdio.h>
struct S
{
	char name[20];
	int age;
	float score;
};
int main()
{
	struct S s = { "张xx",19,100.0f };
	FILE* pf = fopen("test.dat","wb");
	if (pf == NULL)
	{
		perror(pf);
		return 1;
	}
	//写
	fwrite(&s, sizeof(s), 1, pf);
	//关闭
	fclose(pf);
	return 0;
}

效果如下: 

 整数和浮点数以二进制的形式和以文本的形式存储是截然不同的

fread的使用: 

//二进制读写文件
#include<stdio.h>
struct S
{
	char name[20];
	int age;
	float score;
};
int main()
{
	struct S s = {0};
	FILE* pf = fopen("test.dat", "rb");
	if (pf == NULL)
	{
		perror(pf);
		return 1;
	}
	//读
	fread(&s, sizeof(s), 1,pf);
	printf("%s %d %f", s.name, s.age, s.score);
	//关闭
	fclose(pf);
	return 0;
}

效果如下: 

 标准输入流(stdin)与标准输出流(stdout):

         

#include<stdio.h>
int main()
{

	fputc('a', stdout);
    fputc('a', stdout);
	fputc('a', stdout);
	fputc('a', stdout);
	return 0;
}

 

#include<stdio.h>
int main()
{
	int ch = fgetc(stdin);
	printf("%c", ch);

	return 0;
}

 

        


scanf/fscanf/sscanf

printf/fprintf/sprintf

函数sscanf和sprintf

struct S
{
	int a;
	float b;
	char arr[20];
};
#include<stdio.h>
int main()
{
	struct S s = { 99,100.1f,"hello" };
	//把一个结构体转换为字符串
	char arr[200] = {0};
	sprintf(arr,"%d %f %s\n", s.a, s.b, s.arr);
	printf("转为字符串:%s", arr);

	//把字符串转换为格式化数据
	struct S p = { 0 };
	sscanf(arr, "%d %f %s", &(p.a), &(p.b), &p.arr);
	printf("格式化的数据:%d %f %s", p.a, p.b, p.arr);

	return 0;
}

 运行结果

  六个函数的对比:

  • scanf 针对标准输入流(stdin)的格式化的输入函数
  • printf 针对标准输出流(stdout)的格式化的输入函数
  • fscanf针对所有输入流(文件流/stdin)的格式化的输入函数
  • fprintf针对所有输出流(文件流/stdout)的格式化的输入函数
  • sscanf字符串转换成格式化的数据
  • sprintf格式化的数据转换成字符串

今天的讲解就到这里,剩下的下一篇博客见!!!

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
C语言是编程语言中的一朵奇葩,虽已垂垂老矣,但却屹立不倒,诞生了数十年,仍然是最流行的编程语言之一。C语言看似简单,却不易吃透,想要运用好,更是需要积淀。本书是一本修炼C程序设计能力的进阶之作,它没有系统地去讲解C语言的语法和编程方法,而是只对C语言中不容易被初学者理解的重点、难点和疑点进行了细致而深入的解读,揭露了C语言中那些鲜为普通开发者所知的秘密,旨在让读者真正掌握C语言,从而编写出更高质量的C程序代码。 全书一共11章:第1章重点阐述了C语言中不易被理解的多个核心概念,很多初学者在理解这些概念时都会存在误区;第2~8章对预处理、选择结构和循环结构的程序设计、数组、指针、数据结构、函数和文件等知识点的核心问题和注意事项进行了讲解;第9章介绍了调试和异常处理的方法及注意事项;第10章对C语言中的若干容易让开发者误解误用的陷阱知识点进行了剖析;第11章则对所有程序员必须掌握的几种算法进行了详细的讲解;附录经验性地总结了如何养成良好的编码习惯,这对所有开发者都尤为重要。 本书主要内容:  堆和栈、全局变量和局部变量、生存期和作用域、内部函数和外部函数、指针变量、指针数组和数组指针、指针函数和函数指针、传址和传值、递归和嵌套、结构体和共用体、枚举、位域等较难理解的核心概念的阐述和对比;  预处理中的疑难知识点,包括文件的包含方式、宏定义及其常见错误解析、条件编译指令和#pragma指令的使用等;  if、switch等选择结构语句的使用注意事项和易错点解析;  for、while、do while等循环结构语句的使用注意事项和易错点解析;  循环结构中break、continue、goto、return、exit的区别;  一维数组、二维数组、多维数组、字符数组、动态数组的定义和引用,以及操作数组时的各种常见错误解析;  不同类型的指针之间的区别,以及指针的一般用法和注意事项;  指针与地址、数组、字符串、函数之间的关系,以及指针与指针之间的关系;  枚举类型的使用及注意事项,结构体变量和共用体变量的初始化方法及引用;  传统链表的实现方法和注意事项,以及对传统链表实现方法的颠覆;  与函数参数、变参函数、函数调用、函数指针相关的一些难理解和容易被理解错的知识点解析;  文件和指针的使用原则、技巧和注意事项;  函数调用和异常处理的注意事项和最佳实践;  与strlen、sizeof、const、volatile、void、void*、#define、typedef、realloc、malloc、calloc等相关的一些陷阱知识点的解析;  时间复杂度、冒泡排序法、选择排序法、快速排序法、归并排序法、顺序排序法、二分查找等常用算法的详细讲解;  良好的编码习惯和编程风格。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱敲爱骑

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

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

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

打赏作者

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

抵扣说明:

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

余额充值