C语言进阶----->文件的操作

你没有看错,C语言还可以对文件进行操作,C语言官方库提供了一系列的文件操作函数来操作文件,接下来我来带大家了解有关文件相关知识。

本篇博客的重点

1. 为什么使用文件
2. 什么是文件
3. 文件的打开和关闭
4. 文件的顺序读写
5. 文件的随机读写

 

一.为什么使用文件

假如我们用C语言完成了一个学生成绩管理系统,这个系统记录了一个学生的各种信息。可是又有一个问题,我们完成的管理系统需要很好地保存先前的数据。但是一旦我们把运行窗口关掉以后,前一次保存的数据就不存在了,这显然不是我们想要的结果。而要让数据具有长久的生命力的话有两种方式:1.把数据存储到数据库里 2.把数据存储到文件里。正是因为文件的存在,才能够让我们的数据具有长久的生命力,这就是我们为什么要使用文件。

二.什么是文件

磁盘上的文件是文件。但在程序设计里面的文件一般是两种:一.程序文件 二.数据文件

1.程序文件:

包括源文件(后缀名为.c),编译产生的目标文件(后缀名.obj(windows环境下)),可执行程序

(后缀名为.exe)(是二进制文件,更准确一点是二进制的机器指令)

2.数据文件:

这类的文件的内容通常不是程序,而是程序运行中需要读取的数据,或者是输出内容的文件。

而目前我们所写的程序的输出结果都是输出到屏幕上,是以终端为对象,而有的时候我们需要从磁盘里读取内容或者是输出数据存储到磁盘里,这就是本篇博客介绍的数据文件。

三.文件的打开和关闭

在介绍文件的打开和关闭之前,我们有必要提及文件名的概念,文件名的概念如下:

文件名:是文件的唯一标识符,以便用户识别和引用

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

比如C盘下Code文件夹的一个名为test的文本文件的文件名如下:

 c:\code\test.txt
总之,文件名就相当于一个文件的标识符,它唯一指定一个文件

讲完了文件名的概念,接下来我们还要介绍一类指针---->文件指针

缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”
每一个被使用的文件都会在内存中开辟一个文件信息区来存储文件的信息,而这个文件信息区是由一个结构体来维护的!这个结构体是由C语言库的实现者取名为FILE

不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。我们不必太关心FILE的细节,因为每当我们打开一个文件,系统都会自动地为我们创建一个FILE类型的结构体变量来填充文件的相关信息,而我们通常使用一个FILE类型的指针来维护一个文件的信息,比如我们可以创建一个FILE*的变量:

FILE* pf;//定义了一个文件指针变量pf

我们可以通过pf找到文件的信息区,对这个文件进行读取和写入的访问
 

因为C语言和文件相关的操作函数多数需要使用文件指针,因此了解文件指针这个概念是十分重要的,接下来我们就介绍文件打开和文件关闭的两个函数:fopen()和fclose()

fopen函数的函数原型如下:

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

首先这个函数接受两个参数,第一个参数是指定要打开的文件名,第二个参数则是指定打开的模式,一般的,文件的打开模式有如下几种:

下面我们

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
//以"w"方式打开文件
//"w"是写的模式
int main()
{
	FILE* fp=fopen("test.txt", "w");
//打开文件可能失败,而fopen打开失败会返回一个空指针!
	if (NULL == fp)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	return 0;
}

以“w”的形式打开一个名为test的文本文件,在程序运行前,当前目录的文件的情况如下:

 而当我们调用完程序以后,该文件夹目录下的情况就是这样的:

 我们可以发现多了一个名为test的文本文件,在以“w”模式打开文件的时候,假设当前目录下没有同名的文件,则fopen函数将会创建一个同名的文本文件!而当以“r”的模式打开时,如果当前目录下没有同名的文件,则fopen函数打开失败,返回空指针!而“wb”和“rb”则是以二进制写和读取文件,fopen的处理方式和“w”和“r”的模式相同。

注意:假如test.txt文件在别的文件目录下,那么要想打开这个文件,必须写上这个文件的全名,比如我在如下的一个地方创建了一个名为testdemo的文本文件

 我们接下来尝试打开这个文件,并使用fputc函数向里面写入一个字符b

在启动程序之前,testdemo文件的内容什么都没有:

 启动程序以后:

 可见确实是写入了一个字符,说明我们的操作是正确的,也就是在对不和源文件同一个目录下的文件进行操作的时候,我们需要写上它的绝对路径,这样才能正确打开这个文件。

4.文件的顺序读写:

 而顺序读取就是fget函数和fput函数,下面我们来看看这两个函数的函数原型:

int fgetc( FILE *stream );

int fputc( int c, FILE *stream );

fgetc函数是从指定的文件中读取字符,这个读取的顺序是按照文件中字符的顺序,fputc函数则是讲指定的字符c写入到文件中,具体的使用如下代码:

int main()
{  //fputc函数和
//使用fputc函数输入a-f者几个字符:
	FILE* pf=fopen("test.txt", "w");
	if (NULL == pf)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//把a-f写入test.txt中
	for (char ch = 'a'; ch <= 'f'; ++ch)
	{
		fputc(ch, pf);
	}
    fclose(pf);
    pf=NULL;
	return 0;
}

程序运行以后,打开test.txt观察结果:

 我们看到确实test文本文件被写入了abcdef,这就是fputc函数的作用:按顺序往指定的文件里写入数据!

接下来我们看一下fgetc函数的作用,示例代码如下:

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
	FILE* pf=fopen("test.txt", "r");
	if (NULL == pf)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//使用fgetc函数读取
	int ch = 0;
	while ((ch=fgetc(pf))!= EOF)
	{
		printf("%c ", ch);
	}
   fclose(pf);
   pf=NULL;

	return 0;
}

程序结果如下:

 输出了abcdef这和test文本文件里的内容一致!这就是fgetc函数的使用。

注意:

为什么fgetc函数获取的是字符,但是函数的返回值却是int?原因很简单,就是假设返回的值是char类型,那么我们无法区分程序是否读取异常,因为char类型的值的取值返回总是合法的!

2.fputs和fgets函数

C语言还为我们提供了fputs和fgets函数,我们来看一看这两个函数的函数原型;

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

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

类似于fgetc和fputc,fgets是从文件中获取一个指定长度的字符串,而fputs函数的功能则是把一段字符串写入指定的文本文件

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
	FILE* pf=fopen("test.txt", "w");
	if (NULL == pf)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//将hello world写入test.txt文件
	fputs("hello world", pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

接下来,我们使用fgets函数从test文件中读取长度为5的字符串并输入到屏幕上:

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
//fgets函数的使用
int main()
{   
	FILE* pf=fopen("test.txt", "r");
	if (NULL == pf)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	char ch[100] = { 0 };
	fgets(ch, 5, pf);
	printf("%s\n", ch);
	fclose(pf);
	pf = NULL;
	return 0;
}

程序运行结果如下:

 我们发现实际程序只输出了四个字符,也就是说fgets函数虽然指定了读取长度为n,但实际最多读取的字符个数是n-1个,这点需要使用者留心一下。

2对比一组函数:scanf/fscanf/sscanf   printf/fprintf/sprintf

我们来看它们的函数原型:

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

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

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

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

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

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

 通过观察不难发现:sscanf是从字符串读取数据,sprintf是往写入字符串到字符指针指定的空间,而以fscanf和fprintf只是执行的对象由字符换成了文件流!接下来我们使用sprinf函数写入数据,在使用sscanf函数读取并输出

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//sprintf----->写入信息到一个字符串中
struct S
{
	char name[20];
	int a;
	double d;
};
int main()
{   
	struct S s = { "张三",21,95.0};
	char buf[100] = { 0 };
	sprintf(buf, "%s %d %lf", s.name, s.a, s.d);
	printf("%s\n", buf);
	return 0;
}

运行程序结果如下:

 接下来我们在使用sscanf读取数据放到另外一个结构体里并打印观察

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
struct S
{
	char name[20];
	int a;
	double d;
};
int main()
{   
	struct S s = { "张三",21,95.0};
	char buf[100] = { 0 };
	struct S tmp = { 0 };
	sprintf(buf, "%s %d %lf", s.name, s.a, s.d);
	sscanf(buf, "%s %d %lf", tmp.name, &(tmp.a), &(tmp.d));
	printf("tmp:: %s %d %lf\n", tmp.name, tmp.a, tmp.d);
	return 0;
}

程序运行结果如下:

 由此可见当我们需要向字符串格式化写入或读取数据的时候,可以使用sprintf或sscanf函数,而fscanf和fprintf函数和sscanf与sprintf函数仅仅是作用对象不同,在用法方面基本一致,这里就不再演示了。

四.文件的随机读写

前面讲到的一系列文件读写函数都是按顺序执行的,而有的应用场景下我们需要随机读写文件中的内容,为了实现文件的随机读写,C语言的官方函数库为我们提供了文件随机读写函数--->fseek,

ftell,以及rewind这三个函数,下面我们就来看看这三个函数的具体应用。

1.fseek---->根据文件指针的位置和偏移量来定位文件指针。

我们先来看fseek函数的函数原型:

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

第一个参数stream是一个文件指针

第二个参数offet是你指定偏移量

第三个参数则是你开始偏移的起始位置。

而这个起始位置,C语言官方函数库的实现者已经为你定义好了,具体如下:

SEEK_SET----->指向这个文件起始的位置

SEEK_CUR---->当前文件指针的位置

SEEK_END---->文件末尾的标志

接下来我们就通过一个例子来看看怎么使用fseek函数实现随机读写:

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
	FILE* pf = fopen("test.txt", "wb");
	if (NULL == pf)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	fputs("abcdef", pf);
	fseek(pf, -2, SEEK_END);
	//程序应变为abcdhh
	fputs( "hh",pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

运行结果如下:

 我们发现最后两个字母确实被改成了hh,使用实现了随机读写的功能!

2.ftell函数

我们先来看ftell函数的函数原型

long ftell( FILE *stream );

这个函数的作用是返回当前文件指针相对于文件起始位置的偏移量,具体的使用很少,这里就不过多的展开说明

3.rewind

函数原型:

void rewind( FILE *stream );

这个函数的作用是让文件指针重新指向SEEK_SET的位置。

这句语句等价于下面这样一条语句:

fseek(pf,0,SEEK_SET);

用法也比较简单,这里就不具体展开说明了。

希望通过本篇博客能够帮助同学更好的使用C语言来进行文件的操作,希望能够和大家共同进步!

  • 11
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值