C语言文件操作1

1.文件的基础知识

文件

▶︎概念◀︎

文件是指存储在外部存储器上的数据集合。

常见的有:磁盘、U盘等等。

▶︎作用◀︎

保存数据

▶︎文件名◀︎ 

文件是指文件的标识符号,每个文件都有一个文件名。

文件名主要由三部分组成:文件路径+文件名字+文件后缀

▶︎格式◀︎

文件的格式就是文件的后缀,只表示文件的打开方式,并不代表所存数据的类型。

比如:.docx是文档文件、.jpg是图片等等。 

文件的分类 

在C语言中文件被分为两大类:程序文件和数据文件。

▶︎程序文件◀︎

程序文件就是用来存放文件代码的文件,根据程序的不同时期分为以下三类:

  • 源程序文件(.c)代码哥编写的C语言程序文件,里面由敲的代码。
  • 目标文件(.obj)源程序文件经过编译器编译后生成的文件。
  • 可执行程序(.exe)目标文件进行链接后生成的文件,可以直接在操作系统上运行。

▶︎数据文件◀︎

 数据文件就是给人们直接观看的、用户需要使用的,

这里对于C语言而言,就是程序运行时用于存储和检索数据的文件。

数据文件又分为文本文件和二进制文件:

  • 文本文件数据是以字符进行存储的文件,不需要进行转换,都是我们能读懂的字符,这种文件通常就是给我们看的。
  • 二进制文件数据是以二进制形式进行储存的文件,需要将原来的字符转换成对应的ASCII码值,再转换成对应的二进制数字进行储存,这种文件一般打开了是个正常都看不懂的。

▶︎概念◀︎

我的理解

·       在写程序时,通常我们需要在键盘、鼠标和显示器上读取/输入数据,由于每种设备的操作方式都不同,那就意味着我们需要掌握每一种外部设备的读取数据/写入数据的方式,这就搞得很复杂,那么怎么办呢?

        这个解决方法就是“流”“流”是一个抽象出来的概念【记住这句话就行,不是重点】,“流”可以将不同的设备的操作方式进行简化统一

        具体实现可以理解为:将所有的外部设备都放入“流”中,我们只需要从“流”中就可以拿取/写入你想要的数据,至于“流”是如何和这些外部设备打交道的我们无需关心要操作时只需要对流进行操作即可

        而对“流”进行操作,通常都需要先打开“流”,再进行操作,最后关闭“流”

 ▶︎分类◀︎

    “流”是一种拿数据写数据的传输方式它可以看作是一条通道,需要进行数据传输的地方称为流的起源或者终点,“流”起源/终点往大的讲可以是从外部设备,也可以是文件,也可以是一块内存空间,反正流就是用来帮助数据传输,下面介绍以下三种特殊流:

  • stdio标准输入流,该条流是指从键盘输入数据到程序中去,键盘是流的起源,而程序则是流流向的终点;scanf函数则是偷偷的使用了这个流,进行数据读取。
  • stdout标准输出流,该条流是指将程序中的数据打印到屏幕上,可以认为程序是流的起源,而屏幕则是流流向的终点;scanf函数则是偷偷的使用了这个流,进行数据打印。
  • stderr:标准错误输出流,该条流则是将程序的错误信息打印到屏幕上,可以认为程序的错误信息是流的起源,而屏幕则是流流向的终点。
【补充】 

流(Stream)作为一个抽象概念,可以被视为一个连续的字节序列

在计算机科学中,流通常指的是一种数据结构,它能够表示数据的流动。这里的“连续”意味着数据是顺序排列的,形成一个序列,这个序列在逻辑上是连续的,尽管在物理层面上可能分散存储在不同的位置。

3.文件操作步骤

大致操作

        对于文件的操作,实际也是进行文件中的数据操作,前面我们讲过要进行对应的操作,只需找到对应的流即可,那就是文件流,而对于文件流进行就满足打开流,读写流和关闭流,这三大步骤,就是打开文件,读写文件和关闭文件这三大步骤。【知道文件的大致操作即可,前面的不懂没关系】。

        而对于文件的打开、读写和关闭,C语言官方又为我们提供一系列的函数,再介绍这些函数之前现为大家介绍一下文件指针。

文件指针

文件指针是一个指针,指向的是对应的文件

在文件被使用时,就会将对应的文件信息存放在一个结构体中,这个特殊的结构体被称为文件信息,它的指针被称为文件指针,该指针的关键字被系统命名为FILE*。

打开操作

fopen函数

🟡函数作用

Open file.

fopen函数是通过文件以某种方式打开文件,文件的文件指针。

🟢函数定义

FILE * fopen ( const char * filename, const char * mode );
  •  const char * filename          文件名,接收文件名的起始地址,文件名可以包含文件路径。
  • const char * mode                文件的打开方式,函数会提供一些方式,接收的是对应方式名字的地址。
  • FILE*                                       返回类型为该文件的文件指针,失败返回NULL。

函数提供的打开方式如下表:

名称作用文件不存在时
'r'read,只能向文件读取数据,只读打开失败,报错
'w'

创建新的文件,如有已存在的同名文件,同名文件的数据被丢弃,新的文件替换原来的文件。

只能向文件写入数据,只写。

创建新文件,成功打开
'a'append,只能向文件的末尾附加数据打开失败,报错
'r+'向文件读取数据或写入数据,读写打开失败,报错
'w+'

创建新的文件,如有已存在的同名文件,同名文件的数据被丢弃,新的文件替换原来的文件。

向文件读取数据或写入数据,读写

创建新文件,成功打开
'a+'向文件的末尾附加数据或者读取数据打开失败,报错

上面是以文本文件操作的形式进行打开,类似的,可以在上面的基础上再追加字符b,那就表示通过二进制的方式打开,可以在后面追加字符b(rb,r+b等等),也可以在字符r/w/a和'+'之间插入字符b(rb+,wb+等等),具体如下:

名称作用,binary——二进制文件不存在时
"rb"read,二进制形式打开,只能向文件读取数据,只读打开失败,报错
"wb"

创建新的文件,如有已存在的同名文件,同名文件的数据被丢弃,新的文件替换原来的文件。

write,二进制形式打开,只能向文件写入数据,只写

创建新文件,成功打开
"ab"append,二进制形式打开,只能向文件的末尾附加数据打开失败,报错
"rb+"("r+b")

二进制形式打开,向文件读取数据或写入数据,读写

打开失败,报错

"wb+"

("w+b")

创建新的文件,如有已存在的同名文件,同名文件的数据被丢弃,新的文件替换原来的文件。

二进制形式打开,向文件读取数据或写入数据,读写

创建新文件,成功打开

"ab+"

("a+b")

二进制形式打开,向文件的末尾附加数据或者读取数据打开失败,报错

🟣函数使用

//假设存在test.txt的文本文件,里面存有"abcdef"
FILE* p1 = fopen("test.txt","r");
FILE* p2 = fopen("test.txt","w");
FILE* p3 = fopen("test.txt","a");
  1. p1,p2,p3都是test.txt文件的文件指针,但是打开方式不同,创建的文件信息不同,文件指针不同。
  2. p1是只读的方式打开,文件存在不报错。
  3. p2是以只写的方式打开,每次打开时,会创建一个新的文件,如果存在同名文件,会将原文替换掉,访问光标是指着新文件的数据的起始位置
  4. p3是以追加的方式打开,每次打开时,文件的访问光标是指着数据的末尾后的位置

🟤注意事项

  • 函数参数的文件名,可以是单独的文件名字,也可是包含文件路径的文件名
  • 文件以"w"/"w+"/"wb"/"wb+"方式打开时,会创建对应的新文件,如果原来含有同名文件,则新文件将同名文件替换掉,那么原文件的数据就会发生丢失。
  • 当以不同方式打开文件时,意味着文件的属性不同,自然文件信息不同,文件指针就不同。
  • 文件打开可能失败,在打开后的下一步应该先判断指针为否为NULL,再进行读写操作。

【补充1】
打开方式

新的C标准(C2011,不是c++的一部分)添加了一个新的标准子说明符("x"),可以附加到任何"w"说明符(形成"wx", "wbx", "w+x"或"w+bx"/"wb+x")。如果文件存在,此子指定符强制函数失败,而不是覆盖它。

【补充2】 

文件路径


文件路径主要分为两种:绝对路径和相对路径

  • 绝对路径:绝对路径是文件在硬盘上存储的真实路径,它包括了从根目录开始到文件所在位置的所有目录名称
  • 相对路径:相对路径则是基于当前工作目录(程序)或目标文件所在位置的路径,它会随着当前工作目录(程序)的改变而改变;相对路径的表示可以使用./来表示当前目录,或者使用../来表示上一级目录。
  • 路径分隔符:主要分为两类' \ '和' / '在不同的操作系统中,这两者可能有不同的含义,在Windows系统中,通常使用\作为路径分隔符,而在Unix或Linux系统中,则使用/

关闭操作

fclose函数

🟡函数作用

Close file.

fclose函数是用来关闭文件的,通过文件指针对指定文件进行关闭。

🟢函数定义

int fclose ( FILE * stream );
  • FILE * stream              文件指针,要关闭的文件的指针。
  • int                                 返回类型为int,如果关闭成功返回0,失败则返回EOF

🟣函数使用

//接着上面的p1,p2,p3,进行关闭
fclose(p1);
fclose(p2);
fclose(p3);
  1. fclose函数很简单,传要关闭的文件的指针进行关闭就可。

🟤注意事项

  • 打开文件在使用完一定要又文件的关闭。
  • 文件关闭后记得,将文件指针置为NULL,规避野指针。

【补充1】

  • 关闭与流关联的文件并解除关联。
  • 所有与流关联的内部缓冲区都将与流解除关联并刷新:任何未写入的输出缓冲区的内容将被写入,而任何未读的输入缓冲区的内容将被丢弃。
  • 即使调用失败,作为参数传递的流也不再与文件及其缓冲区相关联。
函数作用
fopen以某种方式打开文件,并返回文件指针
fclose关闭文件指针所指向的文件

4.文件的读写操作

C语言官方主要提供了以下几个文件读写操作的函数:

指定流读写字符
函数作用使用范围
fgetc从指定流中读取一个字符所有流
fputc向指定流中写入一个字符所有流
fgets从指定流中读取字符串所有流
fputs向指定流中写入字符串所有流
标准流读写字符
函数作用使用范围
getchar从标准输入流中读取一个字符标准输入流
putchar向标准输出流中写入一个字符标准输出流
gets从标准输入流中读取字符串标准输入流
puts向标准输出流中写入字符串标准输出流

         从上面的两张表格可与看出,fgets/fputs和gets/puts十分相似,只不过前者在前面加了f,这个f就是form的意思,指定了操作的对象,就是指定流输入输出函数,而对于没有f的,则是默认标准输入输出流函数;而对于fgetc/fputc和getchar/putchar,这里对应的却没有类似,事实上getc/putc和fgetc/fputc的作用相同,只不过内部实现的方法不同。

下面为大家重点介绍以下指定流的输入输出函数,以文件流为例:

fgetc和fputc

🟡函数作用

  • fgetc:从指定流中读取(输出)字符。Get character from stream.
  • fputc:向指定流中写入(输入)字符。Write character to stream.

🟢函数定义

int fgetc ( FILE * stream );
  •  FILE * stream             指定流的文件指针,对文件而言就是文件的文件指针
  •  int                               返回类型为int,读取成功返回读取字符的ASCII码值失败返回EOF
int fputc ( int character, FILE * stream );
  •  int character               传入字符,接收时会转为写入字符的ASCII码值
  •  FILE * stream              指定流的文件指针,对文件而言就是文件的文件指针
  •  int                                 返回类型为int,写入成功写入读取字符的ASCII码值失败返回EOF

🟣函数使用

fgetc函数使用

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	//1.打开文件,只读的方式
	FILE* p = fopen("test1.txt", "r");
	//判断是否打开失败
	if (p == NULL)
	{
		perror("fopen");
	}
	//进行读取操作,使用fgetc函数
	int c = 0;//存放函数返回值
	while (c != EOF)//多次读取,读取失败时返回EOF
	{
		c = fgetc(p);//每次读取一个字符后,文件的位置指示符将移动到下一个字符处
		printf("%d ", c);
	}
	//3.关闭文件
	fclose(p);
	//指针置为NULL,规避野指针
	p = NULL;
}
文件位置
运行结果

 fgetc函数使用

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	//1.打开文件,只写的方式
	FILE* p = fopen("test2.txt", "w");
	//判断是否打开失败
	if (p == NULL)
	{
		perror("fopen");
	}
	//进行写入操作,使用fputc函数
	for (char c = 'a';c<='f';c++)//写入6个字符
	{
		int ret = fputc(c,p);//每次读取一个字符后,文件的位置指示符将移动到下一个字符处
		printf("%d ", ret);//打印函数返回值
	}
	//3.关闭文件
	fclose(p);
	//指针置为NULL,规避野指针
	p = NULL;
}
运行结果

🟤注意事项

  • 这两个函数的返回值都是int类型,都是返回int类型,为啥不直接返回字符呢?是为了兼容EOF的值,EOF是设置为-1,是int类型,所以返回类型就都设置为int。
  • fgetc函数读取一个字符后文件的指示光标移动到下一个字符位置下面,fputc也是写入一个字符后移动到下一个字符位置下面
  • fgetc读取成功返回读取到的字符的ASCII码值,fputc写入成功返回写入字符的ASCII码值
  • 对于fgetc读取失败,一是打开文件后没有数据,那就已经到达文件末尾,文件文件光标指向的就是EOF,二是文件读取到了数据的末尾,此时文件光标指向的就是EOF,EOF的意思就是End Of File,意思是文件末尾的标志

fgets和fputs

🟡函数作用

  • fgets:从指定流中读取(输出)字符串(文本)。Get string from stream.
  • fputs:向指定流中写入(输入)字符串(文本)。Write string to stream.

函数作用与fgetc和fputc相似,只不过fgets和fputs是字符串,c就是character,字符的意思,s就是string,字符串的意思。

🟢函数定义

char * fgets ( char * str, int num, FILE * stream );
  • char * str                 str指向读取到的字符串存入的空间
  • int num                   要读取的字符的个数,只读取num-1个有效字符;遇到EOF或换行符结束
  • FILE * stream         指定流的文件指针,对文件而言就是文件的文件指针
  • char *                      返回类型为char*,如果成功返回str的值读取失败则返回NULL
int fputs ( const char * str, FILE * stream );
  • const char * str            str指向要写入字符串所在空间
  • FILE * stream               指定流的文件指针,对文件而言就是文件的文件指针
  • int                                 返回类型为int,写入成功返回一个非负数失败返回EOF

🟣函数使用

fgets函数使用

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void test1()
{
	//1.打开文件
	FILE* p = fopen("test.txt", "r");
	//判断打开是否失败
	if (p == NULL)
	{
		perror("fopen");
	}
	//接受的文本的数组
	char arr[30];
	//2.fgets函数读取数据
	char* c = fgets(arr, 5, p);
	//打印
	printf("%s %p\n", arr, c);
	//3.关闭文件 
	fclose(p);
	//指针置为空
	p = NULL;
	c = NULL;
}
void test2()
{
	//1.打开文件
	FILE* p = fopen("test1.txt", "r");
	//判断打开是否失败
	if (p == NULL)
	{
		perror("fopen");
	}
	//接受的文本的数组
	char arr[30];
	//2.fgets函数读取数据
	char* c = fgets(arr, 5, p);
	//打印
	printf("%s %p\n", arr, c);
	//3.关闭文件 
	fclose(p);
	//指针置为空
	p = NULL;
	c = NULL;
}
void test3()
{
	//1.打开文件
	FILE* p = fopen("test2.txt", "r");
	//判断打开是否失败
	if (p == NULL)
	{
		perror("fopen");
	}
	//接受的文本的数组
	char arr[30];
	//2.fgets函数读取数据
	char* c = fgets(arr, 5, p);
	//打印
	printf("%s %p\n", arr, c);
	//3.关闭文件 
	fclose(p);
	//指针置为空
	p = NULL;
	c = NULL;
}
int main()
{
	//都是从文件中读取5个字符
	test1();//文件中字符长度>5
	test2();//文件中字符长度<5,会读取到文件终止符EOF
	test3();//文件中字符长度>5,但是有换行符
	return 0;
}
运行结果

 fputs函数使用

#include<stdio.h>
int main()
{
	FILE* p = fopen("test3.txt", "w");
	if (p == NULL)
	{
		perror("fopen");
	}
	int ret = fputs("abcdef", p);
	printf("%d\n", ret);
	fclose(p);
	p = NULL;
	return 0;
}

🟤注意事项

  • fgets函数读取过程中,只读取num-1个有效字符,剩余的一个位置被设置为NUL('\0');如果读取过程中遇到文件结尾(EOF)或者换行符,那么读取就结束,同时该字符也会被读取进去。
  • fgets如果读取成功,返回的是str的值;如果读取失败,返回的值为NULL
  • fputs如果读取成功,返回的是一个非零数;如果读取失败,返回的值为EOF(-1)
  • fgets和fputs函数在使用完,文件的指示光标都会移动到使用后的对应位置。

scanf家族和printf家族

🟡函数作用

scanf家族
函数作用使用范围
scanf从标准输入流中读取数据标准输入流
sscanf从字符流中读取数据字符流
fscanf从指定流中读取数据所有流
printf家族
函数作用使用范围
printf将数据写入到标准输出流中标准输出流
sprintf将数据写入到字符流中字符流
fprintf将数据写入到指定流中所有流

🟢函数定义

scanf家族

int scanf ( const char * format, ... );
int sscanf ( const char * s, const char * format, ...);
int fscanf ( FILE * stream, const char * format, ... );
  • const char * format 格式说明符,与后面的变量类型相对应,用于占位和表明格式
  • ...                     输入的参数,类型与前面的格式说明符有一套对应的法则,对于scanf来说这里是取的变量的地址,因为函数只有传址调用才能对指定变量的值进行改变
  • 对于scanf是从标准流中输入到指定变量,则不需要传流,已经隐藏实现了
  • 对于sscanf,是从指定的存放字符变量的空间,将字符输入到指定变量
  • 对于fscanf,是指定的流,使用与之对应的文件指针

 printf家族

int printf ( const char * format, ... );
int sprintf ( char * str, const char * format, ... );
int fprintf ( FILE * stream, const char * format, ... );
  • const char * format 格式说明符,与后面的变量类型相对应,用于占位和表明格式
  • ...                     输入的参数,类型与前面的格式说明符有一套对应的法则
  • 对于printf是输出到标准流中,则不需要传流,已经隐藏实现了
  • 对于sprintf,是将字符输出到对应的空间的地址
  • 对于fprintf,是指定的流,使用与之对应的文件指针

🟣函数使用

scanf家族

//}
#include<stdio.h>
int main()
{
	int a;
	char arr1[10]="abcedf";
	char arr2[10];
	int b;
	scanf("%d", &a);//将在输入流中读取的数据存放在变量a中
	sscanf(arr1, "%s",&arr2);//将在arr中读取的数据存放在变量arr2中
	fscanf(stdin, "%d", &b);//将在stdin(标准输入流)中读取的数据存放在变量b中
	return 0;
}
运行结果

 printf家族

#include<stdio.h>
int main()
{
	int a = 10;
	int b = 2;
	char arr1[10];
	char arr2[10] = "abcdef";
	printf("%d\n", a);//将变量a的值输出到标准输出流上(屏幕)
	sprintf(arr1, "%s", arr2);//将字符数组arr2的值输出到字符数组arr1中
	fprintf(stdout, "%d\n", b);//将变量b的值输出到stdout流上(标准输出流)
	return 0;
}
运行结果

🟤注意事项

  • sscanf函数读取的是缓冲区的数据内容,sprintf函数是将数据写入/存放/输出到缓冲区,而其他的函数则是读写流的数据。
  • 对于scanf家族函数的返回值,如果写入成功,则返回写入成功的项数,如果失败则返回EOF(-1)。
  • 对于printf家族函数的返回值,如果输出成功,则返回输出字符的个数,如果失败则返回负数

本章内容结束,下章见,拜拜!!!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值