C语言中的文件操作你了解多少?快来看看吧[建议收藏]

Posted 呆呆兽学编程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言中的文件操作你了解多少?快来看看吧[建议收藏]相关的知识,希望对你有一定的参考价值。

本片文章我要给大家介绍一下文件是什么文件分为哪几种类型C语言中是如何对文件进行操作
今天我就来给大家介绍一下~
博主码云gitee链接:https://gitee.com/byte-binxin(需要代码自取)


什么是文件

我们经常可以看到我们的磁盘下有很多的文件,那里的文件就是文件。即磁盘上的文件是文件
文件又分为程序文件数据文件

程序文件

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

数据文件

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

文件名

为了让用户能够找到自己的文件,每个文件都应该有一种特定的标识任何一个文件都有文件名。且文件名包含3部分:文件路径+文件名主干+文件后缀
如:C:\\gitee\\code\\test.c
这里的文件路径就是C:\\gitee\\code** ,文件主干名是 test** ,后缀是**.c** 。

文件类型

据数据的组织形式,数据文件被称为文本文件或者二进制文件
数据在内存中以二进制的形式存储并输出到外存就是二进制文件
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件
一个10000在二进制文件中要占用4个字节,在文本文件中以ASCII码的形式输出则要占用5个字节,因为它把每一个数字看成一个字符,所以有五个,就是五个字节。

文件指针

用一个指针变量指向一个文件,这个指针称为文件指针。并且通过这个指针变量对文件进行各种操作。
指针变量定义的形式如下:

FILE *指针变量

FILE实际上是由系统定义的一个结构,该结构中含有文件名、文件状态和文件当前位置等信息。使用者编写源程序时不必关心FILE结构的细节
例如,VS2008编译环境提供的 stdio.h 头文件中有以下的文件类型申明:

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

一般都是通过一个指针变量来维护这个结构体变量,这样使用起来更为方便。

文件的打开与关闭

文件的读写之前,我们必须打开文件,使用结束后都应该关闭文件。
打开文件时我们会用到一个fopen的函数,关闭文件会用到fclose的函数。(ANSIC 规定)

打开文件的时候,fopen函数会返回一个文件指针,这样就很好地把文件与指针建立起联系了。所以我们可以根据返回值来判断文件是否打开正常。
文件的位置可以分为相对路径和绝对路径:
相对路径就是当前文件下目录下的文件,只要写文件主干名+文件后缀
绝对路径就是写文件路径+文件名主干+文件后缀

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("open file failed");
		exit(-1);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

打开文件有很多种方式,以下列举出了一些:

文件使用方式含义如果该文件不存在
“r”(只读)为了输入数据,打开一个已经存在的文本文件出错
“w”(只写)为了输出数据,打开一个文本文件创建一个新文件
“a”(追加)在文件末尾添加数据出错
“rb”(只读)为了输入数据,打开一个已经存在的二进制文件出错
“wb”(只写)为了输出数据,打开一个二进制文件创建一个新文件
“ab”(追加)在二进制文件末尾添加数据出错
“r+”(读和写)为了读和写,打开一个文本文件出错
“w+”(读和写)为了读和写,创建一个文本文件创建一个新文件
“a+”(读和写)打开一个文本文件,在文件末尾进行读和写出错
“rb+”(读和写)为了读和写,打开一个二进制文件出错
“wb+”(读和写)为了读和写,创建一个二进制文件创建一个新文件
“ab+”(读和写)打开一个二进制文件 ,在文件末尾进行读和写出错

文件的顺序读写

知识普及-流

流是一个高度抽象的概念。我们可以把数据读写到外部设备(如:文件,屏幕,网络,光盘,软盘等)时,也可以从这些外部设备上数据读写相关的数据,显然在与不同外部设备的交互方式是肯定存在差异的,所以C语言就抽象出了流这样一个概念。流自己会关注如何把数据读写到哪个设备上,这样程序员就可以不用关注这些细节,只要关注如何把数据写到流里面,这样程序员写代码的代价就减少了不少。

通常,C语言程序运行起来时,会默认打开三个流:

  1. 标准输入流 stdin
  2. 标注输出流 stdout
  3. 标注错误流 stderr

这三个流的类型都是FILE*
所以,我们不需要考虑要打开键盘和屏幕这些流。这也是为什么我们没有打开标注输出流就可可以在屏幕上打印数据等等类似的事情。

字符输出函数fputc

int fputc( int c, FILE *stream );


这个函数是写一个字符到一个流上。适用于所有的流。
下面演示一次文件流:

include <stdio.h>
#include <stdlib.h>

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("open file failed");
		exit(-1);
	}
	//写文件
	fputc('a', pf);
	fputc('b', pf);
	fputc('c', pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

程序运行结果:


三个字符输出到文件上了。
再来看一下标准输出屏流:

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("open file failed");
		exit(-1);
	}
	//写文件
	fputc('a', stdout);
	fputc('b', stdout);
	fputc('c', stdout);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

运行结果如下:

结果输出到屏幕上了。这就是两种流的区别。

字符输入函数fgetc

int fgetc( FILE *stream );


这个函数是从一个流中读取一个字符。适用于所有的流。

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("open file failed");
		exit(-1);
	}
	int ch;
	//读文件
	ch = fgetc(pf);
	printf("%c ", ch);
	ch = fgetc(pf);
	printf("%c ", ch);
	ch = fgetc(pf);
	printf("%c\\n", ch);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

运行结果如下:

再给大家演示一个标准输入流:

int main()
{
	
	int ch;
	ch = fgetc(stdin);
	printf("%c ", ch);
	ch = fgetc(stdin);
	printf("%c ", ch);
	ch = fgetc(stdin);
	printf("%c\\n", ch);
	
	
	return 0;
}

运行结果如下:

文本行输出函数puts

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


这个函数是写一段字符串到一个流中。适用于所有流。

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("open file failed");
		exit(-1);
	}
	//写文件
	fputs("hello world\\n", pf);
	fputs("a good student", pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

运行结果如下:

文本行输入函数gets

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


这个函数是从一个流中得到一段字符串。放入一块空间,第二个参数是最多读取几个字符,适用于所有流。

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("open file failed");
		exit(-1);
	}
	char str[20] = { 0 };
	//读文件
	fgets(str, 20, pf);
	printf("%s", str);
	fgets(str, 20, pf);
	printf("%s\\n", str);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

代码运行结果如下:

格式化输出函数fprintf

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


这个函数是把格式化的数据输出到一个流中。

typedef struct Student
{
	char name[10];
	int age;
	double weight;
}St;

int main()
{
	St st = { "zhangsan", 15, 53.4 };
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("open file failed");
		exit(-1);
	}

	fprintf(pf, "%s %d %.2lf\\n", st.name, st.age, st.weight);

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

	return 0;
}

代码运行结果如下:

格式化输入函数fscanf

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


这个函数是从一个流中读取一段格式化的数据。

typedef struct Student
{
	char name[10];
	int age;
	double weight;
}St;

int main()
{
	St st = {0};
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("open file failed");
		exit(-1);
	}
	//读文件
	fscanf(pf, "%s %d %lf\\n", st.name, &(st.age), &(st.weight));
	printf("%s %d %.2lf\\n", st.name, st.age, st.weight);

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

	return 0;
}

文件的内容原有是

代码运行结果如下:

二进制输出fwrite

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


这个函数的第一个参数是从这里获得数据,第二个参数是一次写入多少个字节的大小的数据,第三个参数是最多写几次,第四个参数是写数据到这个流。返回值是代表这次真是写的次数。只适用于文件流。

typedef struct Student
{
	char name[10];
	int age;
	double weight;
}St;

int main()
{
	St st[2] = { { "zhangsan", 15, 53.4 },{ "lisi", 18, 44.5 } };
	//打开文件
	FILE* pf = fopen("test.txt", "wb");
	if (pf == NULL)
	{
		perror("open file failed");
		exit(-1);
	}
	//写文件
	int i = 0;
	while (i < 2)
	{
		fwrite(&st[i], sizeof(st[i]), 1, pf);
		i++;
	}
	

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

	return 0;
}

运行结果如下:

这是一个二进制文件,我们肉眼是看不懂的,下面我会讲解二进制文件输出,可以把这个文件读出来。

二进制输入fread

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


这个函数是从一个流中读取数据。第一个参数是从读取数据放到这,第二个参数是一次读入多少个字节的大小的数据,第三个参数是最多读几次,第四个参数是从这个流读数据。返回值是代表这次真是写的次数。只适用于文件流。

typedef struct Student
{
	char name[10];
	int age;
	double weight;
}St;

int main()
{
	St st[2] = { 0 };
	//打开文件
	FILE* pf = fopen("test.txt", "rb");
	if (pf == NULL)
	{
		perror("open file failed");
		exit(-1);
	}
	//读文件
	int i = 0;
	while (i < 2)
	{
		fread(&st[i], sizeof(st[i]), 1, pf);
		printf("%s %d %.2lf\\n", st[i].name, st[i].age, st[i].weight);
		i++;
	}


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

	return 0;
}

这里我们就把什么肉眼读不懂的二进制文件读出来了。

对比一组函数

sprintf

先看一下sprintf函数:

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

这个函数是把格式化的数据写到一个字符串中。
看代码实例:

typedef struct Student
{
	char name[10];
	int age;
	double weight;
}St;

int main()
{
	St st = { "zhangsan", 15, 53.4 };
	char str[20];
	sprintf(str, "%s %d %.2lf", st.name, st.age, st.weight);
	printf("%s\\n", str);
	return 0;
}

代码运行结果如下:

sscanf

看一下sscanf函数:

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


这个函数是从一段字符串中读取格式化的数据。
看一个实例:

typedef struct Student
{
	char name[10];
	int age;
	double weight;
}St;

int main()
{
	St st = { 0 };
	char str[20] = "zhangsan 15 53.4";

	sscanf(str, "%s %d %lf\\n", st.name, &(st.age), &(st.weight));
	printf("%s %d %.2lf\\n", st.name, st.age, st.weight);

	return 0;
}

代码运行结果如下:

这一组函数的区别

scanf 从标准输入流(键盘)中读取格式化的数据
fscanf 从所有输入流(键盘和文件)中读取格式化的数据
sprintf 从字符串中读取格式化的数据

printf 把格式化的数据输出到标准输出流(屏幕)上
fprintf 把格式化的数据输出到所有输入流(屏幕和文件)上
sprintf 把格式化的数据转换为对应的字符串中

文件的随机读取

fseek

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

这个函数是将文件指针移动到一个指定的位置。
第一个参数是指要操作的文件流
第二个参数是指从起始位置开始的偏移量
第三个参数是指文件指针的起始位置
第三个参数有三种位置:
SEEK_SET文件开始的位置
SEEK_CUR文件当前的位置
SEEK_END文件的末尾位置
下面来看一个实例:

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("open file failed");
		exit(-1);
	}

	//写文件
	fputs("this is an apple", pf);
	//移动文件指针的位置
	fseek(pf, 10, SEEK_SET);

	fputs("hello ", pf);

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

	return 0;
}

代码运行结果如下:

ftell

long ftell( FILE *stream );

这个函数是为了得到当前位置的偏移量。
看一个实例:

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("open file failed")大神写的代码中肯定都会用方法引用,如果不会快来看看吧(建议收藏)

❤️程序猿必备的数电知识,快来看看你掌握多少!❤️(建议收藏)

❤️程序猿必备的数电知识,快来看看你掌握多少!❤️(建议收藏)

JDK8中新的日期时间工具类真的很好用,还不清楚的快进来看看吧,建议收藏

JDK8中新的日期时间工具类真的很好用,还不清楚的快进来看看吧,建议收藏

Python为什么这么火?你了解多少呢?