🌟菜鸟主页:@晨非辰的主页
👀学习专栏:《C语言学习》
💪学习阶段:C语言方向初学者
⏳名言欣赏:“人理解迭代,神理解递归。”
目录
1. 对文件的介绍
1.1 文件的作用
--程序的数据存储在电脑的内存中,若程序退出,内存回收,数据会丢失,再次运行程序什么也没有。所以,数据存储在文件中,就可以持久保存。
--什么是文件?:磁盘(硬盘)上的文件就是文件。在程序设计中,文件一般分为两种:程序文件、数据文件(文件功能不同)。
1.2 程序文件、数据文件
- 程序文件
包括源程序文件(后缀为.c), 目标文件(windows环境后缀为.obj), 可执行程序(windows环境后缀为.exe);
- 数据文件
文件的内容不一定是程序,而是程序运行时读写的数据。比如程序运行需要从中读取数据的文件,或者输出内容的文件。
--主要介绍数据文件,前面学习所处理数据的输入输出是以终端为对象,即从终端键盘输入数据,屏幕显示结果。
--有时会将信息输出到硬盘上,需要时再读取到内存中使用,这里处理的是磁盘上的文件。
1.3 文件名
--文件有自己的标识,便于进行识别、利用。
文件名包含3部分:文件路径+文件名主干+文件后缀
如:C:\code\test.txt
--C:\code\ ->文件路径;test ->文件名主干;.txt ->文件后缀;
2. 二进制文件和文本文件
--由数据的组织形式,数据文件被分为文本文件和二进制文件。
- 二进制文件:数据在内存中以⼆进制形式存储,不加转换的输出到外存的文件中;
- 文本文件:要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件;
(外存:硬盘、U盘...)
--数据在文件中怎样存储?
- 字符以ASCII形式存储,数值型数据既可用ASCII形式存储,也可用二进制形式存储;
- 整数10000,以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符⼀个字节),而二进制形式输出,则在磁盘上只占4个字节(int型);
--代码演示:(相关操作会在后面介绍)
int main()
{
int a = 10000;
FILE* pf = fopen("data.txt", "wb");//打开文件的操作
if (pf == NULL)
{
perror("fopen");
return 1;
}
fwrite(&a, 4, 1, pf);//将10000以二进制形式写到文件中
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
--想要观察二进制,在vs上打开:
--运行结束会创建一个文件->
--在vs上添加test文本文件、打开:
3. 文件的打开、关闭
3.1 流、标准流
-
流:
--程序数据需要输出到外部设备,也要获取数据,不同外部设备的输入输出操作不同,为方便进行操作,抽象出了->"流"的概念,将”流“想成流淌字符的河。
--C语言对文件、画面、键盘等数据的输入输出操作都是由”流“完成的。一般,向”流“里写数据或读数据,都要打开”流“,在操作。
- 标准流:
--为什么从键盘输入、屏幕输出,我们没有打开”流“?
--C语言在程序启动时默认打开3个”流“:
- stdin - 标准输入流:在大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据;
- stdout - 标准输出流:大多数的环境中输出至显示器界面,printf函数就是将信息输出到标准输出流中;
- stderr - 标准错误流,大多数环境中输出到显示器界面;
--3个流的类型是 -> FILE * ,通常称为文件指针。 C语言中,就是通过 FILE* 的文件指针来维护流的各种操作的。
3.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;
--不同编译器FILE类型包含内容不完全相同,每次打开文件,系统会自动创建FILE结构的变量,并填充信息,不需要我们关心。
--一般是通过一个FILE指针来维护结构变量:
FILE* pf; //文件指针变量
--定义pf是⼀个指向FILE类型数据的指针变量。使pf指向某个文件的文件信息区(结构体变量)。通过该文件信息区中的信息能够访问文件。即—>通过文件指针变量能够间接找到与它关联的文件。
3.3 文件的打开、关闭
--对于文件,读写前要打开,使用结束要关闭:使用 fopen 函数打开文件, fclose 关闭文件。
--编写程序的时,打开文件同时,会返回⼀个FILE*的指针变量指向该文件,相当于建立了指针和文件的关系。
--头文件:<stdio.h>
3.3.1 fopen函数
FILE* fopen (const char* filename, const char* mode);
功能:
用来打开参数 filename 所指定文件,同时将其和一个流进行关联,后续对流的操作是通关 fopen 函数返回的指针来维护的。具体对流的操作是通过参数mode指定的;
参数:
--filename:要打开的文件的路径和名称。可以是相对路径(如
"data.txt"
),也可以是绝对路径(如"C:\\Users\\data.txt"
);(注意:在字符串中使用'\'时,需要用转义字符,写两个'\\'。)
--mode:文件的打开模式。是一个字符串,指定文件是用于读、写、追加,以及是以文本模式还是二进制模式打开。这是 fopen 的核心参数,决定了能对文件进行何种操作;
返回值:一个
FILE*
类型(文件指针)。
文件打开成功,返回指向
FILE
结构的指针。可以用于后续操作中标识对应的流;文件打开失败,返回NULL,要对fopen的返回值进行判断,来验证文件是否成功打开;
--mode--文件操作方式:
文件使用方式 | 含义 | 若指定文件不存在 |
"r"(只读) | 为了输入数据,打开已经存在的文本文件 | 出错 |
"w"(只写) | 为了输出数据,打开文本文件 | 建立新的文件 |
“a”(追加) | 向文本文件尾添加数据 | 建立新的文件 |
“rb”(只读) | 为了输入数据,打开二进制文件 | 出错 |
"wb"(只写) | 为了输出数据,打开二进制文件 | 建立新的文件 |
“ab”(追加) | 向二进制文件尾添加数据 | 建立新的文件 |
“r+”(读写) | 为了读和写,打开文本文件 | 出错 |
“w+”(读写) | 为了读和写 建立新的文件 | 建立新的文件 |
“a+”(读写) | 打开文件,在文件尾进行读写 | 建立新的文件 |
“rb+”(读写) | 为了读和写打开二进制文件 | 出错 |
“wb+”(读 写) | 为了读和写,新建新的二进制文件 | 建立新的文件 |
“ab+”(读 写) | 打开二进制文件,在文件尾进行读和写 | 建立新的文件 |
--演示几种方式,体会函数使用:
- 演示在当前目录下的操作
int main()
{
//打开文件,"w"--不存在,创建文件
FILE* pf = fopen("data.txt", "w");
//这里的路径为相对路径,表示在当前工程目录下的data.txt文件
if (pf == NULL)
{
perror("fopen");
return 1;
}
//后续读取文件操作
//……
//关闭文件
//……
return 0;
}
- 演示在当前目录的上一级目中进行打开文件
./ -- 表示当前目录; ../ --表示当前目录的上一级目录
int main()
{
//打开文件,"r"--不存在,出错
FILE* pf = fopen("./../data.txt", "r");//只读打开,看是否存在
//这里的路径为相对路径,表示在当前工程目录上一级目录下的data.txt文件
//已经将文件剪切到上一级目录中
if (pf == NULL)
{
perror("fopen");
return 1;
}
//后续读取文件操作
//……
//关闭文件
//……
return 0;
}
--结果正常运行
- 演示在当前目录的上一级目录的上一级目录的操作
int main()
{
//打开文件,"r"--不存在,出错
FILE* pf = fopen("./../../data.txt", "r");//只读打开,看是否存在
//这里的路径为相对路径,表示在当前工目录上一级目录的上一级目录下的data.txt文件
//已经将文件剪切到相应目录中
if (pf == NULL)
{
perror("fopen");
return 1;
}
//后续读取文件操作
//……
//关闭文件
//……
return 0;
}
--结果正常运行
- 演示当前目录下上二级目录中某文件夹中文件夹中的操作
int main()
{
//打开文件,"r"--不存在,出错
FILE* pf = fopen("./../../test/test2/data.txt", "r");//只读打开,看是否存在
//这里的路径为相对路径,表示在当前工目录上二级目录下的test文件下test2文件的data.txt文件
//已经将文件剪切到相应目录中
if (pf == NULL)
{
perror("fopen");
return 1;
}
//后续读取文件操作
//……
//关闭文件
//……
return 0;
}
- 演示在桌面上的操作
int main()
{
//打开文件,"r"--不存在,出错
FILE* pf = fopen("C:\\Users\\Tian\\Desktop\\data.txt", "r");//只读打开,看是否存在
//这里的路径为绝对路径,表示在桌面上的data.txt文件
//已经将文件剪切到相应目录中
if (pf == NULL)
{
perror("fopen");
return 1;
}
//后续读取文件操作
//……
//关闭文件
//……
return 0;
}
3.2.2 fclose函数
int fclose(FILE* stream);
功能:
--关闭参数 stream 关联的文件,取消关联关系。与该流关联的所有内部缓冲区均会解除关联并刷新:任何未写入的输出缓冲区内容将被写入,任何未读取的输入缓冲区内容将被丢弃;
参数:
--stream: 这是一个指向
FILE
对象的指针,该对象标识要被关闭的流。指针通常由fopen等函数成功调用后返回的;返回值:成功--返回0、失败--返回EOF;
--注意:
--必须检查返回值;
--一个指针关一次:确保每个由
fopen
打开的FILE*
指针都有且仅有一次对应的fclose
调用。关闭一个已经关闭的指针或一个空指针会导致未定义行为(程序崩溃);--作用域问题: 确保指针失效(如局部变量离开作用域)之前关闭文件。否则永远失去关闭机会,导致资源泄漏(类似于动态内存管理函数的释放、置空)。
--代码演示函数功能:
nt main()
{
//打开文件,"w"--不存在,创建文件
FILE* pf = fopen("data.txt", "w");
//这里的路径为相对路径,表示在当前工程目录下的data.txt文件
if (pf == NULL)
{
perror("fopen");
return 1;
}
//后续读取文件操作
//……
//关闭文件
fclose(pf);
pf == NULL;
return 0;
}
4. 文件的顺序读写(初)
--文件读写会涉及到以下函数:
函数名 | 功能 | 适用于 |
fgetc | 从输入流读取一个字符 | 所有输入流 |
fputc | 向输出流写入一个字符 | 所有输出流 |
fgets | 从输出流读取一个字符 | 所有输入流 |
fputs | 向输出流写入一个字符 | 所有输出流 |
fscanf | 从输入流读取带有格式的数据 | 所有输入流 |
fprintf | 向输出流写入带有格式的数据据 | 所有输出流 |
fread | 从输入流读取一块数据 | 文件输入流 |
fwrite | 从输出流写入一块数据 | 所有文件流 |
4.1 fputc 函数
int fputc(int characer, FILE* stream);
功能:将参数 character 指定的字符写入 stream 指向的输出流,通常用于向文件或标准输出流写入。写入字符之后,会调整指示器。字符会被写入流内部位置指示器当前指向的位置,随后该指示器自动向前移动⼀个位置;
参数:
--character: 要写入的字符。虽然参数类型是 int ,但函数内部会将其转换为
unsigned char
后写入;--stream : 是⼀个FILE*类型的指针,指向了输出流(通常是文件流或stdout);
返回值:--成功: 返回写入的字符(以
int
形式返回);--失败/错误: 返回EOF
(End Of File,通常是 -1)。
int main()
{
//打开文件
FILE* ps = fopen("data.txt", "w");
//判断返回值
if (ps == NULL)
{
perror("fopen");
return 1;
}
//写数据--文件流
fputc('a', ps);
fputc('b', ps);
fputc('c', ps);
//关闭文件
fclose(ps);
ps = NULL;
return 0;
}
//循环输出一系列字符
int main()
{
//打开文件
FILE* ps = fopen("data.txt", "w");
if (ps == NULL)
{
perror("fopen");
return 1;
}
for (int i = 'a'; i <= 'z'; i++)
{
fputc(i, ps);
}
fclose(ps);
ps = NULL;
return 0;
}
int main()
{
//写数据--标准输出流_屏幕
fputc('a', stdout);
fputc('b', stdout);
fputc('c', stdout);
return 0;
}
--打开相应文件进行查看输出:
4.2 fgetc 函数(一次性输入完数据,再回车输出)
int fgetc(FILE* stream);
功能:从参数 stream 指向的流中读取一个字符。函数返回的是文件指示器当前指向的字符,读取这个字符之后,文件指示器自动前进道下⼀个字符;
参数:
--stream: FILE* 类型的文件指针,可以是 stdin、其他输入流的指针。是 stdin 就从标准输入流读取数据。是文件流指针,就从文件读取数据;
返回值:
--成功: 返回读取到的字符(以
unsigned char
转换后的int
形式返回)。--失败/到达文件末尾: 返回
EOF
(设置错误指示器( ferror )/文件结束指示器( feof ))。
int main()
{
//打开文件
FILE* ps = fopen("data.txt", "r");
//检查返回值
if (ps == NULL)
{
perror("fgets");
return 1;
}
//在前面fputc函数操作的基础上,对文件内容进行读取
//读取数据--文件流
for (int i = 0; i < 10; i++)
{
//读取
int c = fgetc(ps);
//将读取的数据打印在屏幕
fputc(c, stdout);
}
//关闭文件
fclose(ps);
ps = NULL;
return 0;
}
输出:abcdefghij
int main()
{
for (int i = 0; i < 4;i++)
{
//读数据--标准输入流
int c = fgetc(stdin);
fputc(c, stdout);
}
return 0;
}
--这里建议一次性输入完数据后,再回车输出!
4.3 feof、ferror 函数
int feof(FILE *stream); // 检查是否到达文件末尾
int ferror(FILE *stream); // 检查是否发生文件错误
- 在读取文件过程中,遇到文件末尾,文件读取结束。读取函数会在对应的流上设置文件结束指示符,指示符可以通过 feof 函数检测。如果 feof 函数检测到指示符已经被设置,则返回非0的值,没有设置则返回0;
- 在读/写文件的过程中,发生了读/写错误,文件读取结束。读/写函数会在对应的流上设置错误指示符,错误指示符可以通过 ferror 函数检测到。如果 ferror 函数检测到错误指示符已被设置,则返回非0的值,没有设置则返回0;
--测试feof函数
int main()
{
FILE* ps = fopen("data.txt", "r");
if (ps == NULL)
{
perror("fopen");
return 1;
}
//读文件-文件:abcdef
int i = 0;
for (i = 0;i < 10;i++)
{
int c = fgetc(ps);
if (c == EOF)
{
if (feof(ps))
{
printf("遇到文件末尾了\n");//到f就没有了
}
else if (ferror(ps))
{
printf("读取发生错误\n");
}
}
else
{
fputc(c, stdout);
}
}
fclose(ps);
ps = NULL;
return 0;
}
4.4 fputs函数
int fputs(const char* str, FILE* stream);
功能:将参数 str 指向的字符串写入到参数 stream 指定的流中(不含结尾空字符 \0 ),用于文件流或标准输出(stdout);
参数:--str : 指针,指向要写入的字符串(必须以 \0 结尾);
--stream : FILE* 的指针,指向要写入字符串的流;
返回值:--成功,返回非负整数;--失败,返回 EOF设置流的错误指示器,用 ferror() 检查原因;
int main()
{
//打开文件
FILE* ps = fopen("data.txt", "w");
//判断
if (ps == NULL)
{
perror("fopen");
return 1;
}
//写数据
fputs("abcd", ps);
//关闭文件
fclose(ps);
ps = NULL;
return 0;
}
4.5 fgets 函数
char* fgets(char* str, int num, FILE* stream);
功能:从 stream 指定输入流中读取字符串,读取到换行符、文件末尾(EOF)或到指定字符数(含结尾空字符 \0 ),然后将读取到的字符串存储到str指向的空间;
参数:
--str :指向字符数组的指针,指向的空间存储读取的字符串。
--num :最大读取字符数(包含结尾的 \0 ,实际最多读取 num-1 个字符)。
--stream :输入流的文件指针(文件流或 stdin )。
返回值:
--成功,返回 str 指针;
--在读取字符时遇到文件末尾,设置文件结束指示器,返回 NULL ,通过 feof()检测;
--发生读取错误,设置流错误指示器,返回 NULL,通过 ferror() 检测。
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
char arr[20] = "xxxxxxxxxxxxxxxx";
fgets(arr, 10, pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
相关系列回顾:
#C语言——学习攻略:攻克 动态内存分配、柔性数组,根本不在话下!
结语:本篇文章到此结束,对于C语言相关知识大家要多次回顾,如果这篇文章对你的学习有帮助的话,欢迎一起讨论学习,你这么帅、这么美给个三连吧~~~