首页 > 其他分享 >C语言-文件操作这一篇足够

C语言-文件操作这一篇足够

时间:2024-09-28 17:50:56浏览次数:8  
标签:文件 NULL 足够 一篇 int C语言 pf FILE fopen

目录

 

1.文件是什么

2.文件类型 

2.1 程序文件 

2.2 数据文件 

 2.3 文件名

3.文件的打开与关闭

 3.1 文件指针

3.2 文件的打开与关闭 

3.2.1 fopen和fclose 

4.文件的顺序读写 

4.1 fgetc和fputc

4.2 fgets和fputs

 4.3 fscanf和fprintf

4.4 fread和fwrite 

5.文件的随机读写

5.1 fseek

5.2 ftell 

5.3 rewind

6. 文本文件与二进制文件 

7.文件读取结束的判定 

7.1 如何判定文件结束

7.2 feof 

8. 文件缓冲区 


1.文件是什么

      上篇博客中我们写了通讯录的实现,但会有一个问题困扰着我们,就是说当这个程序结束之后,信息便也就丢失了,没有进行保存,这也不是我们想要的一个结果,我们希望在程序执行结束之后,下次再执行时,用户信息仍然在里面,这就涉及到了数据持久化的问题,我们一般数据持久化的方法有,把数据存放在磁盘文件、存放到数据 库等方式。今天呢,我们就通过这篇博客来介绍文件操作的内容,把数据存储到磁盘文件当中去。       

2.文件类型 

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

2.1 程序文件 

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

2.2 数据文件 

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

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

 2.3 文件名

        文件名包含3部分:文件路径+文件名主干+文件后缀,平常我们为了方便起见,文件标识常被称为文件名。

3.文件的打开与关闭

 3.1 文件指针

        怎么把文件与我们所写的程序建立一定的联系呢?

        我们首先要知道每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE。一般都是通过一个FILE 的指针来维护这个 FILE 结构的变量,从而对文件进行操作。如下图所示:

3.2 文件的打开与关闭 

        文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。文件的打开与关闭都是成对出现的。

注意:一个程序打开文件资源是有限的,如果打开的文件不关闭最终会导致文件打不开

3.2.1 fopen和fclose 

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

这个函数有两个参数:第一个是所打开的文件名称,可以是文件地址例如:D:\C(hab)\Project1\test1\test1\test2.dat,写成这种形式需要注意\别与转义字符造成混淆。第二个参数是打开的方式,例如下面两个图,可以以"r"(只读),"w"(只写)...等方式进行打开文件。

它的返回值有两种:

       1)成功返回指向这个文件的指针

       2)失败返回NULL

 

注意:fopen这个函数以'w'、'w+'形式打开的时候,会格式化就是把文件的数据清空 

int fclose( FILE *stream );

这个函数结构比较简单,参数就是文件指针,返回值是int类型,返回结果如下:

       1)成功关闭文件就是返回0

       2)失败的话返回EOF

直接上代码:

int main()
{
	//以写的方式打开文件
	FILE* pf = fopen("test2.dat", "w");
    //fopen("D:\\C(hab)\\Project1\\test1\\test1\\test2.dat", "w");该地址填写时需注意\,
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

4.文件的顺序读写 

4.1 fgetc和fputc

 int fgetc( FILE *stream );

这个函数结构也很简单,就是从文件指针所指向的文件里读取一个字符,注意:这是顺序读取。

它的返回值:

       1)读取成功了返回的就是这个字符啊ascll码值

       2)读取失败了就是返回EOF。

int fputc( int c, FILE *stream );

这个函数是往文件里输入一个字符,参数有两个,第一个是我们所输入的字符,第二个是我们所输入字符的文件。

返回值有两个:

       1)成功就是字符被输入,这个字符的ascll码值被返回

       2)失败了就是返回EOF。

这里需要知道一个知识点,的概念 

流可以降低程序员学习成本,不需要对各种各样的硬件进行学习。

C语言程序,只要运行起来,就默认打开了3个流

stdin - 标准输入流 - 键盘

stdout - 标准输出流 - 屏幕

stderr - 标准错误流- 屏幕

int main()
{
	//以写的方式打开文件
	FILE* pf = fopen("test2.dat", "w");
	//fopen("D:\\C(hab)\\Project1\\test1\\test1\\test2.dat", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputc("a", pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

int main()
{
	//读的方式打开文件
	FILE* pf = fopen("test2.dat", "r");
	//fopen("D:\\C(hab)\\Project1\\test1\\test1\\test2.dat", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
    int ret = fgetc(pf)
	printf("%c", ret);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

4.2 fgets和fputs

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

这个函数就是用来读取一个字符串的,参数有三个,第一个就是我们用来存放从文件读取过来的字符串的地址,第二个参数是我们所读取字符的个数,第三个参数是文件指针用来找到我们想从哪个文件去读取数据。

返回值,成功的话返回所读取字符串的起始地址,错误会返回NULL指针。

注意:读取字符串的时候最后位置会放置'\0'

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

这个函数是往一个文件放置字符串的,参数有两个,第一个是我们所放置的字符串,第二个是我们需要放置字符串的文件指针。

返回值:如果成功的话会返回一个非负的值,失败的话会返回EOF。

int main()
{
	//写的方式打开文件
	FILE* pf = fopen("test2.dat", "w");
	//fopen("D:\\C(hab)\\Project1\\test1\\test1\\test2.dat", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputs("abcdef", pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}


int main()
{
	char arr[20] = { 0 };
	//读的方式打开文件
	FILE* pf = fopen("test2.dat", "r");
	//fopen("D:\\C(hab)\\Project1\\test1\\test1\\test2.dat", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	fgets(arr, 5, pf);
	printf("%s\n", arr);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

 4.3 fscanf和fprintf

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

int scanf( const char *format [,argument]... );

这个函数与scanf非常相像,其实就是在scanf的基础之上,加入一个文件指针的参数,具体用法看下例:

struct S
{
	char arr[10];
	int a;
	float b;
};
int main()
{
	struct S s = {0};
	//打开文件
	FILE* pf = fopen("test2.dat", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fscanf(pf, "%s %d %f", s.arr, &s.a, &s.b);
	printf( "%s %d %f", s.arr, s.a, s.b);//	fprintf(stdout, "%s %d %f", s.arr, s.a, s.b)

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

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

int printf( const char *format [, argument]... );

这个函数与printf也非常相像,其实也是在printf的基础之上,加入一个文件指针的参数,具体用法看下例:

struct S
{
	char arr[10];
	int a;
	float b;
};
int main()
{
	struct S s = {"abcde", 10, 5.5f};
	//打开文件
	FILE* pf = fopen("test2.dat", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fprintf(pf, "%s %d %f", s.arr, s.a, s.b);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

注意:我们可以用fprintf去实现printf的功能,如下所示:

 fprintf(stdout, "%s %d %f", s.arr, s.a, s.b)

4.4 fread和fwrite 

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

这个函数就是往文件里以二进制的形式去写数据,最终所展现的就是我们看不懂的形式,它有四个参数:

      1)第一个是我们所要写的数据的地址

      2)第二个参数是数据元素的大小

      3)第三个是数据的个数

      4)第四个就是我们的文件指针

具体用法见下例:

struct S
{
	char arr[10];
	int a;
	float b;
};
int main()
{
	struct S s = {"abcde", 10, 5.5f};
	//打开文件
	FILE* pf = fopen("test2.dat", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fwrite(&s, sizeof(struct S), 1, pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

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

既然有以二进制的形式展现的文件,看不懂,必然程序里有可以读懂的函数,那么fread应运而生,它四个参数,只不过它是从文件指针所指向的文件里读count个size大小的数据。

      1)第一个参数就是我们从文件读取数据后所要存放在哪里的地址

      2)第二个参数就是数据元素的大小

      3)第三个参数就是要读取几个数据

      4)第四个就是我们的文件指针

用法见下例:

struct S
{
	char arr[10];
	int a;
	float b;
};
int main()
{
	struct S s = {0};
	//打开文件
	FILE* pf = fopen("test2.dat", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fread(&s, sizeof(struct S), 1, pf);
	printf("%s %d %f", s.arr, s.a, s.b);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里额外介绍两个函数:sprintf与sscanf

int sprintf( char *buffer, const char *format [, argument] ... );

这个函数是把一个格式化的数据转化成一个字符串,它的参数与printf的唯一区别就在于buffer这个参数,它就是格式化数据转化之后所存储的空间的地址。如下例所示:

struct S
{
	char arr[10];
	int a;
	float b;
};
int main()
{
	struct S s = { "abcde", 10, 5.5f };
	char b[20] = { 0 };
	sprintf(b, "%s %d %f", s.arr, s.a, s.b);
	printf("%s\n", b);
	return 0;
}

int sscanf( const char *buffer, const char *format [, argument ] ... );

这个函数是把一个字符串转化成一个格式化的输出,它的参数与scanf的唯一区别就在于buffer这个参数,它就是字符串转化格式化数据中的字符串空间的地址。如下例所示:

struct S
{
	char arr[10];
	int a;
	float b;
};
int main()
{
	struct S s = { "abcde", 10, 5.5f };
	char b[20] = { 0 };
	struct S temp = { 0 };
	sprintf(b, "%s %d %f", s.arr, s.a, s.b);
	printf("%s\n", b);
	sscanf(b, "%s %d %f", temp.arr, &temp.a, &temp.b); 
	printf( "%s %d %f\n", temp.arr, temp.a, temp.b);
	return 0;
}

scanf、fscanf、sscanf和printf、fprintf、sprintf 

scanf:针对标准输入的格式化的输入语句 - stdin

fscanf:针对所有输入流的格式化的输入语句 - stdin/文件

sscanf:从一个字符串中读取一个格式化的数据

printf:针对标准输出的格式化输出语句 - stdout

fprintf:针对所有输出流的格式化输出语句 - stdout/文件

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

5.文件的随机读写

5.1 fseek

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

这个函数就是fgetc的升级版,它可以随机访问,但是它不返回字符,仅仅是用作调整文件指针,它有3个参数,其中第二个参数就是可以自己去定义偏移量从而访问要访问的字符,关于它的参数如下:

      1)第一个参数stream就是文件指针用来从目标文件中读取字符

      2)第二个参数offset就是偏移量,是偏移起始位置。

      3)第三个参数就是起始位置,它有三种,一、SEEK_CUR,这是指当前位置。二、SEEK_END,这是指结束位置,三、SEEK_SET,这是指起始位置

它的返回值:(1)成功返回0(2)失败返回非0

具体使用如下所示:

int main()
{
	FILE* pf = fopen("test2.dat", "r");//文件设置的是abcde
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fseek(pf, 1, SEEK_CUR);
	int ret = fgetc(pf);
	printf("%c\n", ret);//b
	fclose(pf);
	pf = NULL;
	return 0;
}

5.2 ftell 

long ftell( FILE *stream );

返回文件指针相对于起始位置的偏移量,参数就是文件指针

int main()
{
	FILE* pf = fopen("test2.dat", "r");//文件设置的是abcde
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fseek(pf, 1, SEEK_CUR);
	int ret = fgetc(pf);
	printf("%c\n", ret);//b
	printf("%d\n", ftell(pf));//2
	fclose(pf);
	pf = NULL;
	return 0;
}

5.3 rewind

void rewind( FILE *stream );

让文件指针的位置回到文件的起始位置。使用也比较简单。

int main()
{
	FILE* pf = fopen("test2.dat", "r");//文件设置的是abcde
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fseek(pf, 1, SEEK_CUR);
	int ret = fgetc(pf);
	printf("%c\n", ret);//b
	printf("%d\n", ftell(pf));//2
	rewind(pf);
	printf("%d\n", ftell(pf));//0
	fclose(pf);
	pf = NULL;
	return 0;
}

6. 文本文件与二进制文件 

文本文件:就是内存中的数据经过转化转变成ascll码值的形式在文件当中进行呈现的文件

二进制文件:就是内存中的数据不经过任何的转化直接在文件当中进行呈现的文件

数据在内存中的存储:字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。

7.文件读取结束的判定 

7.1 如何判定文件结束

我们可以使用fgetc、fgets、fread等函数的返回值进行判定

fgertc:函数在读取失败结束的时候,会返回EOF,正常读取的时候,返回的是读取到的字符的ASCII码值

fgets:函数在读取失败结束的时候,会返回NULL,正常读取的时候,返回存放字符串的空间起始地址

fread:数在读取的时候,返回的是实际读取到的完整元素的个数,如果发现读取到的完整的元素的个数小于指定的元素个数,这就是最后一次读取了

7.2 feof 

int feof( FILE *stream );

它主要应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。

它的返回值:

      1)如果读取到最后正常结束就是返回一个非0值

      2)没有读到最后就是返回的是0,此时文件读取失败了

注意:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。

int main()
{
	FILE* pf = fopen("test2.dat", "r");
	if (pf == NULL)
	{
		return;
	}
	FILE* pd = fopen("test1.dat", "w");
	if (pd == NULL)
	{
		fclose(pf);
		pf = NULL;
	}
	char ch = 0;
	while ((ch = fgetc(pf)) != EOF)
	{
		fputc(ch, pd);
	}
	if (feof(pf))
	{
		printf("文件正常读取结束\n");
	}
	else if (ferror(pf))
	{
		printf("文件读取失败结束\n");
	}
	fclose(pf);
	pf = NULL;
	fclose(pd);
	pd = NULL;
	return 0;
}

8. 文件缓冲区 

什么叫做文件缓冲区?

所谓缓冲文件系统是指系统自动地在内存中为程序 中每一个正在使用的文件开辟一块“文件缓冲区”

其实就是当我们的程序想要把数据存储到文件当中会先放到文件缓冲区当中,只有文件缓冲区存储满之后才会从文件缓冲区中向文件存储数据,读取时也是如此。如下图所示:

有什么用处呢?其实操作系统每时每刻都在执行语句,当我们要把数据存储到文件当中,就会打断操作系统,存一个打断一下存一个打断一下,效率极其低下,有了缓冲区当我们缓冲区存储满之后,操作系统一下全部存储到文件当中,那这样效率就会提高了。

标签:文件,NULL,足够,一篇,int,C语言,pf,FILE,fopen
From: https://blog.csdn.net/qq_53706413/article/details/142467679

相关文章

  • C语言-动态内存管理(malloc、calloc、realloc)
    目录1.内存布局2.动态内存函数2.1malloc2.1.1malloc是什么2.1.2如何用​编辑2.2free2.2.1free是什么2.2.2如何用2.3calloc2.3.1calloc是什么2.4realloc2.4.1realloc是什么2.4.2realloc如何使用2.4.3realloc可以实现与malloc同样的功能3.常见的动态......
  • C语言指针plus版
            本篇文章,我们将一起探讨指针高级版一、指针数组、数组指针    1.1指针数组    就是存放指针的数组,因此指针数组是一个数组,不是指针    例如:   int*pa[5];   //整型指针的数组   char*pb[2];  //字符型指针......
  • C语言语句
    C语言语句C语言中的的代码是由一条条语句构成,而基本语句分为:•空语句•表达式语句•函数调⽤语句•复合语句•控制语句空语句简而言之,无语句,一个分号为一条语句#include<stdio.h>//主函数intmain(){ ;//空语句 return0;}表达式语句表达式语句......
  • 初始C语言
    C语言程序设计初始C语言初始C语言主要讲解其基础语法、常量变、运算符等等,这是C语言最基本得语法结构,这些是支撑一个程序的必需品,是重中之重。基础语法C语言的令牌主要包括以下几种类型:关键字(Keywords)标识符(Identifiers)常量(Constants)字符串字面量(StringLiterals)......
  • 程序员如何搞副业,零基础入门到精通,收藏这一篇就够了
    随着互联网的发展,程序员也越来越深入到各个行业当中。有了技术的加持,更加激发探索新的领域兴趣。当掌握一定的技术,也许很多程序员也开始考虑创业,将毕生所学的技术能力创造出新的价值。01创业的几点思路1、利用互联网创业。互联网行业投资风险小,因为可以有效的把握市场走......
  • 安全运营工程师是做什么的?零基础入门安全运营(非常详细)看一篇就够了!
    前言什么是安全运营工程师安全运营工程师是网络安全大方向下网络安全运行与维护岗的一个细分岗位。1、安全运营工程师需要具备哪些综合能力?对安全行业发展趋势及生态有良好的认知;具备优秀的逻辑分析能力、表达能力、文字撰写的能力;拥有良好的跨团队协调和推进项目的......
  • 实验1 c语言输入输出和简单程序编写
    任务1:task1.11#include<stdio.h>2345intmain()6{7printf("o\n");8printf("<H>\n");9printf("II\n");10printf("o\n"......
  • 【C语言用筛选法求质数】
    C语言用筛选法求质数筛选法,另一种思路的求质数方法上面的方法数越大判断次数越多,运算时间越长,效率越差,如果对于给定的一个集合,可以用筛选法,思路是将集合中的非质数(合数)标出来,余下来的就是质数了。给定的字符数组charprime[100]={0};,初始化为0,默认全是质数:-)!prime[0]=......
  • 【C语言标准库函数】标准输入输出函数详解2:字符串输入输出
    目录一、字符串输入函数1.1.gets函数(已废弃)1.1.1.函数简介1.1.2.注意和废弃原因1.2.fgets函数1.2.1.函数简介1.2.2.使用场景1.2.3.注意事项1.2.4.示例二、字符串输出函数2.1.puts函数2.1.1.函数简介2.1.2. 使用场景2.1.3.注意事项2.1.4.示例2.2.......
  • C语言 16 系统库
    前面了解了如何使用#include引入其他文件,接着来了解一下系统提供的一些常用库。字符串计算字符串长度:#include<stdio.h>#include<string.h>intmain(){char*c="HelloWorld!";//使用strlen计算长度,注意返回值类型是size_t(别名而已,本质上就是unsignedlong)......