【C语言】文件操作

本文详细介绍了C语言中文件的打开、关闭、标准流(stdin、stdout、stderr)以及各种文件操作函数如fputc、fgetc、fputs、fgets等的使用方法,同时强调了feof和ferror的区别及其在文件读取结束判定中的作用。
摘要由CSDN通过智能技术生成

1. 文件的打开和关闭

1.1 流和标准流

1.1.1 流

我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输入输出操作各不相同,为了方便程序员对各种设备进行操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河。

C程序针对文件、画面、键盘等的数据输入输出操作都是通过流操作的。

一般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作。

1.1.2 标准流

那为什么我们从键盘输⼊数据,向屏幕上输出数据,并没有打开流呢?

那是因为C语⾔程序在启动的时候,默认打开了3个流:
• stdin - 标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。

• stdout - 标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出流中。

• stderr - 标准错误流,⼤多数环境中输出到显⽰器界⾯。

这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的。
stdin、stdout、stderr 三个流的类型是: FILE * ,通常称为文件指针
C语⾔中,就是通过 FILE*文件指针来维护流的各种操作的。

1.2 文件指针

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

每个被使⽤的⽂件都在内存中开辟了⼀个相应的文件信息区,⽤来存放⽂件的相关信息(如⽂件的名字,⽂件状态及⽂件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名 FILE

例如,VS2013 编译环境提供的 stdio.h 头文件中有以下的⽂件类型申明:

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

typedef struct _iobuf FILE;

不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。

每当打开⼀个文件的时候,系统会根据文件的情况⾃动创建⼀个FILE结构的变量,并填充其中的信息,使用者不必关心细节。

⼀般都是通过⼀个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。

下⾯我们可以创建⼀个FILE*的指针变量:

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

定义pf是⼀个指向FILE类型数据的指针变量。可以使pf指向某个⽂件的⽂件信息区(是⼀个结构体变量)。通过该⽂件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够间接找到与它关联的文件。

1.3 文件的打开和关闭

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

在编写程序的时候,在打开⽂件的同时,都会返回⼀个FILE*的指针变量指向该⽂件,也相当于建⽴了指针和⽂件的关系。
ANSI C 规定使⽤ fopen 函数来打开⽂件, fclose 来关闭⽂件。

//打开⽂件
FILE * fopen ( const char * filename, const char * mode );
//关闭⽂件
int fclose ( FILE * stream );

mode表⽰⽂件的打开模式,下⾯都是⽂件的打开模式:

df71a813909a426596a2ab7d0010ae53.png

//例子
#include <stdio.h>
int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件

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

	return 0;
}

2. 文件的顺序读写

2.1 顺序读写函数介绍

405c71eb7d8d4632a5f171777facb696.png

上⾯说的适⽤于所有输⼊流⼀般指适⽤于标准输⼊流和其他输⼊流(如⽂件输⼊流);所有输出流⼀般指适⽤于标准输出流和其他输出流(如⽂件输出流)。

2.1.1 fputc

int fputc ( int character, FILE * stream );

代码:

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	//fputc('a', pf);
	//fputc('b', pf);
	//fputc('c', pf);

	char ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		fputc(ch, pf);
	}

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

	return 0;
}

结果:

64eba97f08234c479ea650122b4bc8da.png

2.1.2 fgetc

int fgetc ( FILE * stream );

代码:

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	int ch = 0;
	while ((ch = fgetc(pf)) != EOF)
	{
		printf("%c", ch);
	}

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

	return 0;
}

运行结果:

0e076769042e4a9b91dc182a494c2cf4.png

2.1.3 fputs

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

代码:

int main()
{
	//1. 打开文件
	FILE*pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputs("hello world\n", pf);
	fputs("i love you\n ", pf);


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

	return 0;
}

结果:

c946c721c1114ab599cec57a7046b245.png

2.1.4 fgets

char * fgets ( char * str, int num, FILE * stream );

代码:

int main()
{
	//1. 打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	char arr[20] = { 0 };

	while (fgets(arr, 20, pf) != NULL)
	{
		printf("%s", arr);
	}


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

	return 0;
}

运行结果:

6649f963e49e48ae85d8530bd1d9fd8f.png

2.1.5 fprintf

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

代码:

#include <stdio.h>

struct S
{
	char name[20];
	int age;
	float score;
};

int main()
{
	struct S s = { "张三", 20, 65.5f };
	//想把s中的数据存放在文件中
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件 - 是以文本的形式写进去的
	fprintf(pf, "%s %d %f", s.name, s.age, s.score);

	fclose(pf);
	pf = NULL;

	return 0;
}

结果:

7e74992fe0e6481392df3d431f18fd16.png

2.1.6 fscanf

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

代码:

struct S
{
	char name[20];
	int age;
	float score;
};

int main()
{
	struct S s = { 0 };
	//想从文件test.txt中读取数据放在s中
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件 
	fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));

	//打印在屏幕上看看
	//printf("%s %d %f\n", s.name, s.age, s.score);//
	fprintf(stdout, "%s %d %f\n", s.name, s.age, s.score);

	fclose(pf);
	pf = NULL;

	return 0;
}

运行结果:

8148343526fb42c791fb8b3f5a0c141e.png

2.1.7 sprintf

int sprintf ( char * str, const char * format, ... );

代码:

struct S
{
	char name[20];
	int age;
	float score;
};


int main()
{
	char buf[200] = { 0 };
	struct S s = { "张三", 20, 65.5f };
	sprintf(buf, "%s %d %f", s.name, s.age, s.score);

	printf("1以字符串的形式: %s\n", buf);//1

	struct S t = {0};
	sscanf(buf, "%s %d %f", t.name, &(t.age), &(t.score));
	printf("2按照格式打印  : %s %d %f\n", t.name, t.age, t.score);//2

	return 0;
}

运行结果:

798228db03004192a8d63903d9d165f2.png

2.1.8 fwrite

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

代码:

int main()
{
	int arr[] = { 1,2,3,4,5 };

	FILE*pf = fopen("test.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写数据
	int sz = sizeof(arr) / sizeof(arr[0]);
	fwrite(arr, sizeof(arr[0]), sz, pf);//以二进制的形式写进去的

	fclose(pf);
	pf = NULL;

	return 0;
}

结果(这是二进制):

0e3cf5a55ad14aa0a1a3308d834db451.png

2.1.9 fread

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

代码:

//以二进制的形式读取

int main()
{
	int arr[5] = {0};

	FILE* pf = fopen("test.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读数据
	fread(arr, sizeof(arr[0]), 5, pf);//以二进制的形式写进去的
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", arr[i]);//1 2 3 4 5
	}

	fclose(pf);
	pf = NULL;

	return 0;
}

运行结果:

fd8384e5f884417e9d099eca62e4b07c.png

3. 文件的随机读写

3.1 fseek

根据⽂件指针的位置和偏移量来定位⽂件指针(⽂件内容的光标)。

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

8eb257ba61de4d66926da23862e55201.png

代码:

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	int ch = fgetc(pf);
	printf("%c\n", ch);//a

	//fseek(pf, 4, SEEK_CUR);
	//fseek(pf, 5, SEEK_SET);
	fseek(pf, -4, SEEK_END);

	ch = fgetc(pf);
	printf("%c\n", ch);//f

	fclose(pf);
	pf = NULL;

	return 0;
}

文本:

6625007cef3649aea194cf233d6cd4a7.png

运行结果:

349364202c4a475ba62f949a18e7b4ae.png

3.2 ftell

返回⽂件指针相对于起始位置的偏移量

long int ftell ( FILE * stream );

代码:

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	int ch = fgetc(pf);
	printf("%c\n", ch);//a
	//fseek(pf, -4, SEEK_END);
	fseek(pf, 0, SEEK_END);
	printf("%d\n", ftell(pf));//这样是不是也能得出字符个数

	fclose(pf);
	pf = NULL;

	return 0;
}

运行结果:

9fae7b4681ab49d898383d603f22e6c2.png

3.3 rewind

让⽂件指针的位置回到⽂件的起始位置

void rewind ( FILE * stream );

代码:

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	int ch = fgetc(pf);
	printf("%c\n", ch);//a
	fseek(pf, -4, SEEK_END);
	ch = fgetc(pf);
	printf("%c\n", ch);//f

	rewind(pf);
	ch = fgetc(pf);
	printf("%c\n", ch);//a

	fclose(pf);
	pf = NULL;

	return 0;
}

运行结果:

a6814b0698294a6498f8d87aabb194f8.png

4. 文件读取结束的判定

4.1 被错误使用的 feof

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

feof 的作用是:当文件读取结束的时候,判断是读取结束的原因是否是:遇到文件尾结束。

1. ⽂本⽂件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )

例如:

• fgetc 判断是否为 EOF .

• fgets 判断返回值是否为 NULL .


2. ⼆进制⽂件的读取结束判断,判断返回值是否⼩于实际要读的个数。

例如:

• fread判断返回值是否⼩于实际要读的个数。

代码1:

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读取
	int ch = 0;
	while ((ch = fgetc(pf)) != EOF)
	{
		printf("%c\n", ch);
	}
	//判断是什么原因导致读取结束的
	if (feof(pf))
	{
		printf("遇到文件末尾,读取正常结束\n");
	}
	else if (ferror(pf))
	{
		perror("fgetc");
	}

	return 0;
}

运行结果:

1a8e52b39c0d441096c89922a2c0c206.png

代码2:

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写
	char ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		fputc(ch, pf);
	}

	//判断是什么原因导致读取结束的
	if (feof(pf))
	{
		printf("遇到文件末尾,读取正常结束\n");
	}
	else if (ferror(pf))
	{
		perror("fputc");
	}

	return 0;
}

运行结果:

07e4bc729bc14ce9809456f7cd35578a.png

代码3:

//拷贝文件:test1.txt  ---> test2.txt

int main()
{
	FILE* pfin = fopen("test1.txt", "r");
	if (pfin == NULL)
	{
		perror("fopen:test1.txt");
		return 1;
	}
	FILE* pfout = fopen("test2.txt", "w");
	if (pfout == NULL)
	{
		fclose(pfin);
		perror("fopen:test2.txt");
		return  1;
	}
	//读文件和写文件
	int ch = 0;
	while ((ch=fgetc(pfin)) != EOF)
	{
		fputc(ch, pfout);
	}
	//
	fclose(pfin);
	pfin = NULL;
	fclose(pfout);
	pfout = NULL;

	return 0;
}

这里可以自己往test1.txt文件中写数据。

代码4:

int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写
	char ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		fputc(ch, pf);
	}

	//判断是什么原因导致读取结束的
	if (feof(pf))
	{
		printf("遇到文件末尾,读取正常结束\n");
	}
	else if (ferror(pf))
	{
		perror("fputc");
	}

	return 0;
}

  • 36
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值