【C语言】文件的输入和输出

本文介绍了C语言中文件的基本概念,包括文件指针和流。详细讲解了文件的打开、读写和关闭函数,如fopen、fclose、fputc、fgetc等,并通过实例展示了文本模式和二进制模式的操作。同时强调了文件操作的注意事项,如文件路径的处理和文件关闭的重要性。

在这里插入图片描述


前言

文件是当今计算机系统不可或缺的部分。它可以用来存储程序,文档,数据等不同种类的信息。
因此作为一位程序员,必须学会文件的相关知识。本文重点介绍从c语言的角度来操作文件。


一、文件介绍

1.1、文件是什么

文件通常指在硬盘或固态硬盘上的一段已命名的区域
比如我们用c语言编写程序时,用到的源文件头文件,我们对文件的定义了解这些就已经足够(除非你未来选择编写操作系统)

———————————————————————————————

1.2、文本模式和二进制模式

C语言把文件看作一系列的连续的字节,每个字节都能被单独读取。这与UNIX操作系统中的文件结构相当应,但考虑到在其他环境中可能并不能完全适应该模型,因此c语言提供两种文件模式:

文本模式二进制模式

首先我们先要区分文件类型
在这里插入图片描述
其中为了规范文本文件的操作,c语言提供了访问文件途径:文本模式和二进制模式。
至于二者的区别,要从不同操作环境进行解释,感兴趣的朋友可以自行查阅,本文重点介绍文件操作。


二、文件操作

先看关键概念,再看文件操作函数

2.1、关键概念

2.1.1、文件指针

每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名
字,文件状态及文件当前的位置等),这些信息是保存在一个结构体变量中的。该结构体类型是由系统
声明的,取名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;

vs2022下的stdio.h对文件类型的声明
 typedef struct _iobuf
    {
        void* _Placeholder;
    } FILE;

在这里插入图片描述

———————————————————————————————

2.1.2、流(stream)

了解文件操作之前,我们先要介绍一个抽象的概念:

流的产生:当我们写了一个程序,想把它应用的不同的设备,由于不同的设备的读写形式不同,对应的程序也不同,这就要求程序员编写不同的程序,这显然对程序员很不友好。
因此,在程序与设备之间加了一层,即,程序将数据传给再将数据传给对应的设备。
在这里插入图片描述


当一个c语言程序运行时,默认打开三个流

  1. 标准输入流:stdin 对应 键盘
  2. 标准输出流:stdout 对应 屏幕
  3. 标准错误流:stderr 对应 屏幕

当看到这,我想读者一定会想到之前学过的输入输出函数:
如scanf 从stdin流获取数据, printf 将数据给 stdout流,可以说我们早就已经用到了流。

介绍完流的概念,我们将正式介绍文件操作函数,这意味这我们不再只是进行 键盘 -> 程序 ->屏幕 这一个单调的过程,可以加上文件了。

———————————————————————————————

2.2、文件操作函数

2.2.1、文件打开函数-fopen()

fopen的坤函数形式:FILE *fopen( const char *filename, const char *mode );
参数1:文件名
参数2:文件使用方式

使用举例:

FILE *pf = fopen("D:\\暂存\\test.txt", "r");

下面介绍其中的意思
先介绍参数2

参数2 - 文件使用方式

文件使用方式作用文件不存在时
r仅对文本文件进行读取操作,不改变文件内容出错
w清除文件所有内容,再文本文件进行写入操作,文件内容改变创建一个新文件,再写入
a在该文件内容末尾,文本文件进行写入操作,追加文件内容创建一个新文件,在写入
rb,wb,ab对应的文本文件改为二进制文件,其余不变不变
r+,w+,a+,rb+,wb+,ab+操作改为即可以读取又可以写入不变

注意使用时要加双引号


参数1:

  1. 我们平时看到的文件路径:D:\学习资料 中间为 单斜杠
    但是fopen中需要用\\,这是用到了转义字符,即D:\\学习资料
  2. 但参数1只写一个文件名也可以
    若参数2为r,rb,rb+,必须要求该文件与你编写的源文件在同一目录下。
    若参数2为w,wb,w+,wb+.a,a+,ab,ab+,若该文件不在同一目录下,则在你编写的源文件所处的目录下创建一个新文件进行操作。列如我在timu.c中 使用 FILE* pf = fopen("sfw.txt","w");
    在这里插入图片描述
    总结:最好加上文件路径

2.2.2、文件关闭函数-fclose()

库函数形式:int fclose( FILE *stream );
作用:关闭文件

#include <stdio.h>
#include <assert.h>
int main(void)
{
    //打开文件
	FILE* pf = fopen("sfw.txt", "w");
	assert(pf);//检验打开文件是否成功
	
	//写文件
	//……
	
	//关闭文件
	fclose(pf);
	pf = NULL;//习惯:把野指针手动制空
	return 0;
}

关于关闭文件的必要性:
一个程序能打开的文件是有限的,打开多了而不改变,则会打不开新文件
文件打开,便记得要关闭,这个习惯对我们未来工作很重要。

———————————————————————————————

2.2.3、文件写入和读取函数

操作单个字符 - 文本模式

fputc() - 写一个字符
库函数形式:int fputc( int c, FILE * stream ); 返回该字符
作用:写入一个字符到文件或屏幕(stdout)

  1. 到屏幕
#include <stdio.h>
int main(void)
{
	fputc('b', stdout);
	fputc('\n', stdout);
	int ret = fputc('a', stdout);
	printf("%c", ret);
	return 0;
}

在这里插入图片描述


  1. 到文件
#include <stdio.h>
#include <assert.h>
int main(void)
{
	FILE* pf = fopen("sfw.txt", "w");
	assert(pf);
	fputc('c', pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述
———————————————————————————————

fgetc() - 读一个字符
库函数形式:int fgetc(FILE * stream);
作用:从文件或键盘(stdin)读取一个字符

  1. 从键盘
#include <stdio.h>
int main(void)
{
	int n = fgetc(stdin);
	printf("%c\n", n);
	int a = fgetc(stdin);
	printf("%c\n", a);
	int b = fgetc(stdin);
	printf("%c\n", b);
	return 0;
}
  1. 从文件
    在这里插入图片描述
#include <stdio.h>
#include <assert.h>
int main(void)
{
	FILE* pf = fopen("sfw.txt", "r");
	assert(pf);
	
	int ret = fgetc(pf);
	printf("%c", ret);

	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述
——————————————————————————————————————————

操作多个字符 - 文本模式

fgets() - 获取一行
char * fgets( char * string, int n, FILE * stream );
作用:从文件或键盘(stdin)读取(n-1)个字符
n即最大字符数 = 字符数+\0
失败返回NULL

列如:
在这里插入图片描述

——————————————————————————————————————————

fputs()
库函数形式:int fputs( const char * string, FILE * stream );
作用:写入一个字符串到文件或屏幕(stdout)
在这里插入图片描述

——————————————————————————————————————————

fseek() - 文件的随机读写
库函数: int fseek(FILE * stream, long offset, int origin);
特别:可以将文件指针移到指定位置.

  1. SEEK_CUR : 文件指针当前位置
  2. SEEK_END : 文件末尾
  3. SEEK_SET : 文件最开始的位置
    第二个参数 向什么方向偏移 : -1向左偏移, 2向右偏移

ftell() - 告诉偏移量是多少
库函数形式:long ftell(FILE * stream);


rewind - 使文件指针回到其实位置
**库函数形式:void rewind( FILE *stream );


在这里插入图片描述

#include <stdio.h>
#include <assert.h>
int main(void)
{
	FILE* pf = fopen("D:\\code\\.c\\study_01\\test.txt", "r");
	assert(pf);
	//fgetc函数执行完,会使文件指针向后移一位
	printf("%c %d\n", fgetc(pf), ftell(pf)); //a 0
	printf("%c %d\n", fgetc(pf), ftell(pf)); //b 1
	
	fseek(pf, 2, SEEK_CUR);  
	printf("%c %d\n", fgetc(pf), ftell(pf)); //e 4
	
	fseek(pf, -1, SEEK_CUR);   
	printf("%c %d\n", fgetc(pf), ftell(pf)); //e 4

	rewind(pf);
	printf("%c %d\n", fgetc(pf), ftell(pf)); //a 0
	
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

——————————————————————————————————————————

为什么fgetc(),fputc()……返回值是int类型?
1. 字符返回的是ASCII码值,因此可以用int类型
2. 获取失败返回EOF,EOF本质是-1,不是ASCII码,因此不能用char类型
举例:fgetc读到字符0xFF时转换成32位为0x000000FF返回 ,代码中用 char类型的ch接收,0x000000FF被截断,ch等于0xFF。 EOF值为 - 1,即0xFFFFFFFF, 与值为 0xFF(即 char 类型 - 1的表示方式)的ch比较时,ch转换成32位后为0xFFFFFFFF(32系统中int 类型 - 1的表示方式),比较结果为相等,导致判断失误,程序提早退出。

——————————————————————————————————————————

二进制模式操作文本文件

fwrite() - 二进制写文件
库函数形式 size_t fwrite(const void * buffer, size_t size, size_t count, FILE * stream);
返回写入的字符个数

#include <stdio.h>
struct S
{
	char arr[10];
	int num;
	float sc;
};
int main(void)
{
	struct S s = { "abcdef", 10, 5.5f };//字符串以二进制写入,
	//和以文本写入是一样的,故文本编译器提取时可以打印成正常所见
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fwrite(&s, sizeof(struct S), 1, pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述


fread() - 以二进制读
库函数形式:size_t fread( void * buffer, size_t size, size_t count, FILE * stream );
返回读取的元素个数

在这里插入图片描述

#include <stdio.h>
struct S
{
	char arr[10];
	int num;
	float sc;
};
int main(void)
{
	struct S s = {0};//字符串以二进制写入,
	//和以文本写入是一样的,故文本编译器提取时可以打印成正常所见
	FILE* pf = fopen("D:\\个人规划\\test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fread(&s, sizeof(struct S), 1, pf);
	printf("%s %d %f\n", s.arr, s.num, s.sc);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述
——————————————————————————————————————————

结尾

文件相关函数不止这些,还有fprintf(),fscanf(),feof()……感兴趣的朋友可自行查阅。学完文件之后,可以尝试去做一下小程序,如通讯录,尝试将数据保存在文件中。
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值