
文章目录
前言
文件是当今计算机系统不可或缺的部分。它可以用来存储程序,文档,数据等不同种类的信息。
因此作为一位程序员,必须学会文件的相关知识。本文重点介绍从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语言程序运行时,默认打开三个流
- 标准输入流:stdin 对应 键盘
- 标准输出流:stdout 对应 屏幕
- 标准错误流: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:
- 我们平时看到的文件路径:
D:\学习资料中间为 单斜杠
但是fopen中需要用\\,这是用到了转义字符,即D:\\学习资料- 但参数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)
- 到屏幕
#include <stdio.h>
int main(void)
{
fputc('b', stdout);
fputc('\n', stdout);
int ret = fputc('a', stdout);
printf("%c", ret);
return 0;
}

- 到文件
#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)读取一个字符
- 从键盘
#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;
}
- 从文件

#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);
特别:可以将文件指针移到指定位置.
- SEEK_CUR : 文件指针当前位置
- SEEK_END : 文件末尾
- 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()……感兴趣的朋友可自行查阅。学完文件之后,可以尝试去做一下小程序,如通讯录,尝试将数据保存在文件中。

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




99

被折叠的 条评论
为什么被折叠?



