目录
[1. 为什么使用](#1. 为什么使用)
[2. 文件概念](#2. 文件概念)
[2.1 程序文 件](#2.1 程序文 件)
[2.2 数据文件](#2.2 数据文件)
[2.2.1 文本文件](#2.2.1 文本文件)
[2.2.2 二进制文件](#2.2.2 二进制文件)
[3. 流的概念](#3. 流的概念)
[3.1 流](#3.1 流)
[3.2 标准流](#3.2 标准流)
[1. 文件指针的使用(fopen和fclose)](#1. 文件指针的使用(fopen和fclose))
[2. 文件读写顺序](#2. 文件读写顺序)
[3. 读写函数介绍](#3. 读写函数介绍)
[3.1 fputc字符输出fputs字符串输出](#3.1 fputc字符输出fputs字符串输出)
[3.2 fgetc字符输入fgets字符串输入](#3.2 fgetc字符输入fgets字符串输入)
[3.3 fprintf对文本输出](#3.3 fprintf对文本输出)
[3.4 fscanf从文本输入](#3.4 fscanf从文本输入)
[3.5 fwrite写二进制到文本](#3.5 fwrite写二进制到文本)
[3.6 fread读取二进制文本](#3.6 fread读取二进制文本)
[4. 读取完成或失败的判断](#4. 读取完成或失败的判断)
[4.1 fgetc读取完的判断](#4.1 fgetc读取完的判断)
[4.2 fgets读取完的判断](#4.2 fgets读取完的判断)
[1. fseek指向某个位置](#1. fseek指向某个位置)
[2. ftell判断光标与起始偏移量](#2. ftell判断光标与起始偏移量)
[3. rewind重置到起始](#3. rewind重置到起始)
一、c语言中的文件
1. 为什么使用
文件是用来存储数据信息的,因为我们程序结束后,数据会全部消失,内存全部回收,无法寻找,这个时候就需要数据文件来存储信息(用于持久化保存)。
2. 文件概念
2.1 程序文件
程序文件就是我们执行代码的.c文件、windows环境的.obj文件和可执行.exe文件。
2.2 数据文件(最后提一下数据文件概念)
2.2.1 文本文件
文本文件就是通过将字符(一个字节)对应的ascll值保留到文件中,这个过程需要将字符(数字也会当成字符)转换为一个一个的ascll值,然后进行存储每个字符对应的ascll值的二进制。
2.2.2 二进制文件
二进制文件,会将一个数据的二进制存储起来,不做任何修改。
两个文件的区别图示:

数据文件,就是存储 信息,这个数据文件的信息是可以通过程序文件进行读取和修改的,。可以通过程序文件与数据文件传输数据来保留和修改数据,最主要的就是可以保留程序运行时产生的数据。
3. 流的概念
3.1 流
我们程序需要输出输入外部设备的内容,而每个外部设备不一样,为了方便写代码,这里引用了流的概念。可以把流当成流淌字符的河,用于流淌数据传输过去。

3.2 标准流
一般编译器默认打开这几个标准流,stdin这个输入流,stdout这个输出流,还有一个错误流stderr。这几个流类型都是FILE*这个指针类型,统称文件指针。
二、c语言中文件的使用
1. 文件指针的使用(fopen和fclose)
fopen是打开文件的函数,如果打开失败,那就会返回NULL这个空指针,而如果打开成功,那就可以进行读写等操作。格式为FILE* pf = fopen("文件名","读取方式"),文件是由路径+文件主干+后缀,fopen文件名可以直接写成文件主干+后缀,表示在当前文件中寻找这个需要修改或者读取的文件名。文件路径包0括范围路径、绝对路径还有相对路径。这里绝对路径就是将整个文件名都表示出来了,把这个文件的所在地方都表示
出来了,在程序中打开文件,我们需要将/这些转义字符转换/这个普通斜杠;而范围路径指的是当前文件中寻找这个文件;相对路径也是范围路径的一种,就是以当前路径作为参考物,取上一个路径或者上上个路径,这个时候就需要..来表示上个路径(首先用.来表示当前路径)。
cs
FILE* pf = fopen("C:\\Users\\bearone\\Desktop\\test.c")//绝对路径
\User\bearone\Desktop\test.c
cs
FILE* pf = fopen("./test.c","r");//当前路径
FILE* pf = fopen("./../test.c","r");//上个路径
FILE* pf = fopen("./../../test.c","r");上上个路径
fclose是用于关闭文件的函数,格式为fclose("FILE* ")。一般关闭后我们需要将这个文件指针置空(空指针)。
cs
#include<stdio.h>
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
else
{
printf("打开文件成功");
}
写文件
fwrite(&a, 4, 1, pf);//以二进制输入到指针文本
fclose(pf);
pf = NULL;
}
2. 文件读写顺序

3. 读写函数介绍
写之前,我给大家展示一个图,方便好理解
3.1 fputc字符输出fputs字符串输出
fputc用来输出字符到文本 的函数,而fputs是输出字符串到文本 中去。格式为(int)fputc(int(字符自动转换为ascll值),文件指针),而(int成功返回非零值,失败返回NULL)fputs(const 类型*,文件指针)。
fputc实例
cs
#include<stdio.h>
int main()
{
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
for (int i = 'a'; i <= 'z'; i++)
{
fputc(i, pf);
}
fclose(pf);
pf = NULL;
return 0;
}
fputs实例
cs
#include<stdio.h>
int main()
{
char str[] = "abcdefghijklmnopqrstuvwxyz";
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fputs(str, pf);
fclose(pf);
pf = NULL;
return 0;
}
均输出到文本为:
3.2 fgetc字符输入fgets字符串输入
fgetc是从文本输入字符到程序 中,每一次读取都会使得光标往后移,而fgets是从文本输入字符串到程序中 。fgetc格式为(int)fgetc(文件指针),fgets格式char* fgets(char* str,int(读取最多字符个数),文件指针),这里fgets是指将文本中的信息输入到char* str这个指针中,这里读取的最大字符个数,如果读取最大个数小于实际中的个数那就会读取不完全,如最大读取为10,那么就会读取9个字符,如果读取最大个数大于实际中的个数,那就会读到最后一个字符就停止,最大10个,实际5个,那就会读到第6取\0(这个\0是必须加的,fgets会自动补充)。fgets只会读取到一行中的字符串 ,如果第二行还有字符,并且还没到达最大字符个数,它也不会继续读取(注意fgetc会读取第二行)。
fgetc实例 :
cs
#include<stdio.h>
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char ch = 0;
while ((ch = fgetc(pf)) != EOF)
{
printf("%c", ch);
}
fclose(pf);
pf = NULL;
return 0;
}
fgets实例:
cs
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE* pf = fopen("test.txt", "r");//打开文件
if (pf == NULL)
{
perror("fopen");
return 1;
}
char* str = malloc(27);//开辟
if (str == NULL)
{
perror("malloc");
return 1;
}
fgets(str, 27, pf);
printf("%s", str);
fclose(pf);
free(str);
str = NULL;
pf = NULL;
return 0;
}
3.3 fprintf对文本输出(扩展sprintf)
fprintf这个用于格式化输出到流 中,将程序文件打印到对应流中。格式为(int)fprintf(FILE*stream,这里printf一样)。
cs
#include<stdio.h>
struct Stu
{
char name[20];
char id[20];
};
int main()
{
struct Stu s1 = { "zhansan","2403020650" };
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fprintf(pf, "name is %s\nid is %s", s1.name, s1.id);//打印到文件指针中
fprintf(stdout, "name is %s\nid is %s", s1.name, s1.id);//打印到屏幕中(标准流)
fclose(pf);
pf = NULL;
return 0;
}
sprintf直接输出字符串到对应的指针中。 代码实例
cs
#include<stdio.h>
int main()
{
char arr[30];
int id = 24030206;
sprintf(arr, "id = %d", id);
printf(arr);
return 0;
}
3.4 fscanf从文本输入(扩展sscanf)
fscanf只不过意义变了格式化输入,格式都和scanf差不多,fscanf是向通过流输入到程序文件中 。fscanf(FILE*stream,和scanf使用一样)。
cs
#include<stdio.h>
struct Stu
{
char name[20];
char id[20];
}s1;
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fscanf(pf, "%s %s", s1.name, s1.id);//文件输入
fscanf(stdin, "%s %s", s1.name, s1.id);//键盘输入
printf("%s\n%s\n", s1.name, s1.id);
fclose(pf);
pf = NULL;
return 0;
}
sscanf是读取指针中数据到后面的变量中去。代码实例
cs
#include<stdio.h>
int main()
{
char arr[30];
char id1[] = "2403020650";
char id2[20];
sprintf(arr, "id1=%s\n", id1);//右输出到左
printf(arr);
sscanf(arr, "%s", &id2);//左输入到右
printf("%s\n", id2);
return 0;
}

3.5 fwrite写二进制到文本
fwrite以二进制的形式写到文本 中,也就是二进制文本。格式为fwrite(const void* ,szie_t(一个元素所占字节大小),size_t (元素个数),FILE*(流))。将指针中二进制放入流中,返回值为放入元素的个数。
cs
#include<stdio.h>
int main()
{
int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
//打开文件
FILE* pf = fopen("test.txt", "wb");//以二进制写
if (pf == NULL)
{
perror("fopen");
return 1;
}
//使用
fwrite(arr, sizeof(int), 9, pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}


小端存储
3.6 fread读取二进制文本
fread就是读取二进制文件 ,将二进制文本读取到程序中来。格式和fwrite一模一样,fread(const void* ,size_t (一个元素占的字节大小),size_t (元素个数),FILE*(流))。返回的是读取元素个数(可以通过比较读取个数是否和自己输入个数一样来判断读取完毕)。
cs
#include<stdio.h>
int main()
{
int arr1[] = { 0,1,2,3,4,5,6,7,8,9 };
//打开文件
FILE* pf = fopen("test.txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//使用
fwrite(arr1, sizeof(int), 9, pf);
//关闭文件
fclose(pf);
pf = NULL;
int arr2[10];
pf = fopen("test.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fread(arr2, sizeof(int), 9, pf);
for (int i = 0; i < 9; i++)
{
printf("%d ", arr2[i]);
}
//关闭
fclose(pf);
pf = NULL;
return 0;
}


这些函数区别。
4. 读取完成或失败的判断
4.1 fgetc读取完的判断
fgetc读取成功会返回对应的ascll值,失败或者读取到文件末尾则会返回EOF。
cs
#include<stdio.h>
int main()
{
//打开
FILE* pf = fopen("test.txt", "w");//写
if (pf == NULL)
{
perror("fopen");
return 1;
}
//使用
for (int i = 'a'; i <= 'z'; i++)
{
fputc(i, pf);
}
//关闭
fclose(pf);
pf = NULL;
char ch = 0;
//打开
pf = fopen("test.txt", "r");//读
if (pf == NULL)
{
perror("fopen");
return 1;
}
//使用
while ((ch = fgetc(pf)) != EOF)
{
printf("%c", ch);
}
printf("\n");
if (feof(pf))
{
printf("读完\n");
}
else if (ferror(pf))
{
printf("错误\n");
}
//关闭
fclose(pf);
pf = NULL;
return 0;
}
4.2 fgets读取完的判断
fgets读取成功就会返回该指针的起始地址,失败或读取到文件末尾则返回NULL空指针。
feof和ferror这俩个区别,feof对于读取失败错误信息,它会返回0,而对于正常读取文件结束,会返回非零。ferror就和它相反,失败就返回非零,成功读取结束返回0;
三、文件随机读取
1. fseek指向某个位置
fseek用于定位指针指向的某个位置,意思就是可以将光标移动到某一位,格式为fseek(FILE*(流),long int offset(相对于某个位置偏移量),int origin(SEEK_SET起始位置)(SEEK_OUR当前光标)(SEEK_END文件末尾))),通过对origin使用和偏移量的使用,将光标移动到想要的位置上。
cs
#include<stdio.h>
int main()
{
//打开
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//使用
char ch = 0;
ch = fgetc(pf);
printf("%c\n", ch);
fseek(pf, -3, SEEK_END);
ch = fgetc(pf);
printf("%c\n", ch);
//关闭
fclose(pf);
pf = NULL;
return 0;
}

2. ftell判断光标与起始偏移量
ftell用于计算此时光标与起始位置的偏移量。格式为(long int)ftell(FILE*流)。
cs
#include<stdio.h>
int main()
{
//打开
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//使用
char ch = 0;
ch = fgetc(pf);
printf("%c\n", ch);
fseek(pf, -3, SEEK_END);
ch = fgetc(pf);
printf("%c\n", ch);
long int r = ftell(pf);
printf("%d\n", r);
//关闭
fclose(pf);
pf = NULL;
return 0;
}

3. rewind重置到起始
rewind将光标重置到起始位置 。格式为rewind(FILE*流)。
cs
#include<stdio.h>
int main()
{
//打开
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//使用
char ch = 0;
ch = fgetc(pf);
printf("%c\n", ch);
fseek(pf, -3, SEEK_END);
ch = fgetc(pf);
printf("%c\n", ch);
rewind(pf);
long int r = ftell(pf);
printf("%d\n", r);
//关闭
fclose(pf);
pf = NULL;
return 0;
}

四、文件缓存区
用于提高效率。
