????????终端是计算机系统中与用户进行交互的界面。
?
? ? ? ? 在以往的程序中,我们通过终端用键盘输入数据,通过屏幕输出信息。
????????但是,如果我们不想手动低效地输入数据,而是通过文件一次性高效输入;
????????如果我们不想让数据丢失,而是把输出的数据存储到文件中时,
????????就需要使用一种新的操作——文件操作。
????????文件是计算机系统中的一种数据存储形式;
????????如文本、图像、音频、视频等。
????????程序运行时,我们写的程序的数据? 是存储在电脑的内存中;如果程序退出,内存会被操作系统回收,于是数据就丢失了;等再次运行程序,无法得到上次程序的数据的。
????????如果要将数据进行持久化的保存,就要使用文件。
????????文件可以由应用程序创建、读取、编辑、删除,它们是计算机系统中存储和传输数据的基本单位;
????????文件可以被命名并存储在计算机的存储介质中;
????????文件还可以根据其格式和扩展名来区分不同类型的文件, 如.txt文件是纯文本文件。
????????
?
????????文件名是用来标识和区分文件的名称。它是文件系统中文件的唯一标识符。
? ? ? ? 文件名的作用是以便用户识别和引用。
文件名包含3部分:文件路径+文件名主干+文件后缀
例如: c:\code\test.txt
重点:在C源文件中引用文件名要注意用两个斜杠来转义为一个斜杠
转义字符:"? ?\\? ? ?==? ? ?\? ?"
????????但是在程序设计中,我们?般谈的文件有两种:程序文件、数据文件。
????????程序文件是包含计算机程序代码的文件。
????????例如:".c"文件表示C语言程序文件, ".java"文件表示Java程序文件, ".py"文件表示Python程序文件等等。
? ? ? ? ?但是,程序文件不是我们要深入讨论的文件,因为我们是在程序文件中写代码,而进行文件操作需要的文件不是程序文件,而是数据文件。
? ? ? ? 文件的内容不?定是程序代码,也可以是程序运行时读写的数据,比如一些文件在程序运行时被程序读取,同时接收并存储程序输出的数据?—— 这些提供输入数据和接受输出数据的文件就是数据文件。
? ? ? ? 我们之前 所处理数据的输入输出都是以终端为对象的,即从终端的键盘输?数据,运行结果从终端显示到显示器(屏幕)上。
????????其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件,这就是使用文件的意义。
????????根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
????????数据在内存中以?进制的形式存储,如果不加转换的输出到外存,就是二进制文件。? ? ? ? 通常情况下,我们无法阅读二进制文件
????????如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的?件就是文本文件。
如何形象理解二进制文件与文本文件呢?
? ? ? ? 假如我们将10000 , ‘a’? ?等存入内存中,那么他在内存中是怎么存储的呢?
? ? ? ? 对于字符,由于有ASCII表的存在,无论是二进制形式,还是文本形式,都是一样的;
? ? ? ? 对于数字,就有所不同了,以10000为例,看一看他在内存中是怎么存储的呢?
例子:
? ? ? ? 当以ASCII形式:每一个位都需要一个字节,需要5个字节;
? ? ? ? 当以二进制形式:10000默认是整形,只需要4个字节。
数字在内存中的存储还是有很大不同的。
??到这里,我们理一下思路——>
小结:
?
?????????为了更好的讲解文件操作,我们先要引入“流”的概念;
? ? ? ? 我们的程序存在的意义就是处理问题,也就是说:????????需要程序从外部获取信息,经过处理之后输出信息。但是不同的设备输入信息的渠道是不同的,为了方便程序从外部获取信息,并且输出信息,C语言抽象出流的概念。
????????C程序针对?件、画?、键盘等的数据输?输出操作都是通过流操作的。
?????????般情况下,我们要想向流?写数据,或者从流中读取数据,都是要打开流,然后操作,之后再关闭流。
那为什么我们从键盘输?数据,向屏幕上输出数据,并没有打开流呢?
那是因为C语言程序在启动的时候,默认打开了3个流:
? stdin-标准输?流,在?多数的环境中从键盘输?,scanf函数就是从标准输?流中读取数据。
? stdout-标准输出流,?多数的环境中输出?显?器界?,printf函数就是将信息输出到标准输出流中。————————标准输入输出流,说白了就是键盘和屏幕。
? stderr-标准错误流,?多数环境中输出到显?器界?。
? ? ? ? 默认打开这三个流后,我们使?scanf、printf等函数就可以直接进?输?输出操作的。
????????stdin、stdout、stderr三个流的类型是: FILE* ,通常称为?件指针。
????????C语?中,就是通过 FILE* 的?件指针来维护流的各种操作的。
????????每个被使?的?件都在内存中开辟了?个相应的?件信息区,?来存放?件的相关信息(如?件的名字,?件状态及?件当前的位置等)。这些信息是保存在?个结构体变量中的。该结构体类型是由系统声明的,取名FILE.
????????
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
例如,VS2013提供的 <stdio.h> 头?件中有以上的?件类型申明:
????????不同的C编译器的FILE类型包含的内容不完全相同,但是?同?异。
??????? 每当打开?个?件的时候,系统会根据?件的情况?动创建?个FILE结构的变量,并填充其中的信息,使?者不必关?细节。
? ? ? ? 这个结构体储存了文件的关键信息,我们通过对结构体内对象的操作,间接实现对文件的操作:
对文件在读写之前应该先打开?件,在使?结束之后应该关闭?件。
????????使用fopen函数打开文件,在打开?件的同时,会返回?个FILE*的指针变量指向该?件,也相当于建?了指针和?件的关系。
ANSIC规定使? fopen 函数来打开?件, fclose 来关闭?件。
?
//打开?件
FILE * fopen ( const char * filename, const char * mode );
//关闭?件
int fclose ( FILE * stream );
文件的打开有多种方式,mode表??件的打开模式,下?都是?件的打开模式:
?件使??式 | 含义 | 如果指定?件不存在 |
“r”(只读) | 为了输?数据,打开?个已经存在的?本?件 | 出错 |
“w”(只写) | 为了输出数据,打开?个?本?件 | 建??个新的?件 |
“a”(追加) | 向?本?件尾添加数据 | 建??个新的?件 |
“rb”(只读) | 为了输?数据,打开?个?进制?件 | 出错 |
“wb”(只写) | 为了输出数据,打开?个?进制?件 | 建??个新的?件 |
“ab”(追加) | 向?个?进制?件尾添加数据 | 建??个新的?件 |
“r+”(读写) | 为了读和写,打开?个?本?件 | 出错 |
“w+”(读写) | 为了读和写,建议?个新的?件 | 建??个新的?件 |
“a+”(读写) | 打开?个?件,在?件尾进?读写 | 建??个新的?件 |
“rb+”(读写) | 为了读和写打开?个?进制?件 | 出错 |
“wb+”(读 写) | 为了读和写,新建?个新的?进制?件 | 建??个新的?件 |
“ab+”(读 写) | 打开?个?进制?件,在?件尾进?读和写 | 建??个新的?件 |
? ? ? ? “r” “w” “a”?是三个基本的打开方式—— 只写“r” “w” “a”表示打开文本文件, “rb” “wb” “ab”表示打开二进制文件。
???????? “r+” “w+” “a+”???????? “rb+” “wb+” “ab+”是复合的操作,是同时能够读和写的打开方式。
?
? ? ? ? 文件的读写有两种类型:顺序读写与随机读写?
对于顺序读写,C语言提供了标准库函数,他们包含在<stdio.h>中。
?
顺序读写函数介绍
函数名 | 功能 | 适?于 |
fgetc | 字符输?函数 | 所有输?流 |
fputc | 字符输出函数 | 所有输出流 |
fgets | ?本?输?函数 | 所有输?流 |
fputs | ?本?输出函数 | 所有输出流 |
fscanf | 格式化输?函数 | 所有输?流 |
fprintf | 格式化输出函数 | 所有输出流 |
fread | ?进制输? | ?件 |
fwrite | ?进制输出 | ?件 |
从这些函数声明中,我们就能大致了解这些函数的使用方法了;?
?
?
?
?
?
?
?如果想要进一步深入了解,请看Cplusplus.com
(stdio.h) - C++ Reference (cplusplus.com)https://legacy.cplusplus.com/reference/cstdio/
文件的打开方式是前提,读写是操作,文件关闭是习惯,指针置空是与人拉开的差距。
?
????????上?说的适?于所有输?流?般指适?于标准输?流和其他输?流(如?件输?流);所有输出流?般指适?于标准输出流和其他输出流(如?件输出流)
? ? ? ? 为了便于理解,这里给出一些应用实例:
#include<stdio.h>
#include<stdlib.h>
struct re_col
{
char name[20];
char us_n[20];
char us_s[20];
int age;
};
typedef struct re_col R;
int main()
{
printf("请输入注册信息>用户名-账号-密码-年龄\n");
FILE* pf = fopen("C:\\Users\\35587\\Desktop\\register collum","w");
if(pf == NULL)
{
perror("fopen");
return 1;
}
R* p = (R*)malloc(sizeof(R)*1);
if(p == NULL)
{
perror("malloc");
return 1;
}
scanf("%s%s%s%d",p->name,p->us_n,p->us_s,&(p->age));
fprintf(pf,"%s %s %s %d",p->name,p->us_n,p->us_s,p->age);
fclose(pf);
pf = NULL;
free(p);
p = NULL;
return 0;
}
? ? ? ? ?根据输入,把信息存放到文件 register collum 中;
#include<stdio.h>
int main1()
{
FILE* pf = fopen("C:\\Users\\35587\\Desktop\\register collum","r");
if(pf == NULL)
{
perror("fopen");
return 1;
}
int ch = fgetc(pf);
if(feof(pf))
{
puts("EOF had been reached");
}
else if(ferror(pf))
{
puts("read failure");
}
printf("%d",ch);
fclose(pf);
pf = NULL;
return 0;
}
int main2()
{
FILE* pf = fopen("C:\\Users\\35587\\Desktop\\register collum","aw");
if(pf == NULL)
{
perror("fopen");
return 1;
}
fputs("iiiii\n",pf);
fclose(pf);
pf = NULL;
return 0;
}
int main3()
{
FILE* pf = fopen("C:\\Users\\35587\\Desktop\\register collum","a");
if(pf == NULL)
{
perror("fopen");
return 1;
}
fputs("abni",pf);
fclose(pf);
pf = NULL;
return 0;
}
int main4()
{
FILE* pf = fopen("C:\\Users\\35587\\Desktop\\register collum","r");
if(pf == NULL)
{
perror("fopen");
return 1;
}
char str[50] = {0};
fgets(str,6,pf);
fprintf(stdout,"%s\n",str);
fclose(pf);
pf = NULL;
return 0;
}
int main5()
{
FILE* pf = fopen("C:\\Users\\35587\\Desktop\\register collum","aw");
if(pf == NULL)
{
perror("fopen");
return 1;
}
fputs("watink",pf);
fclose(pf);
pf = NULL;
return 0;
}
int main6()
{
FILE* pf = fopen("C:\\Users\\35587\\Desktop\\register collum","r");
if(pf == NULL)
{
perror("fopen");
return 1;
}
char str1[50];
fscanf(pf,"%s",str1);
printf("%s\n",str1);//检验是否读取成功
fclose(pf);
pf = NULL;
return 0;
}
int main7()
{
FILE* pf = fopen("C:\\Users\\35587\\Desktop\\register collum","w");
if(pf == NULL)
{
perror("fopen");
return 1;
}
fprintf(pf,"abcded\n");
fclose(pf);
pf = NULL;
return 0;
}
int main()
{
FILE* pf1 = fopen("C:\\Users\\35587\\Desktop\\register collum","r");
if(pf1 == NULL)
{
perror("fopen_1");
return 1;
}
FILE* pf2 = fopen("data_copy.txt","w");
if(pf2 == NULL)
{
fclose(pf1);
pf1 = NULL;
perror("fopen_2");
return 1;
}
//op
char ch;
while((ch = fgetc(pf1)) != EOF)
{
fputc(ch,pf2);
}
fclose(pf1);
pf1 = NULL;
fclose(pf2);
pf2 = NULL;
return 0;
}
?
? ? ? ? 随机读写,实际意义是我们可以控制读写的位置,而不是真的“随机”。?
fseek
根据?件指针的位置和偏移量来?定位??件指针。
ftell
返回?件指针相对于起始位置的偏移量
rewind
让?件指针的位置回到?件的起始位置
?对rewind的应用实例:
int main()
{
FILE* pf = fopen("C:\\Users\\35587\\Desktop\\data.txt","a+");
if(pf == NULL)
{
perror("fopen");
return 1;
}
char str[50];
fgets(str,6,pf);
printf("%s\n",str);
int c = ftell(pf);
printf("%d\n",c);
rewind(pf);//回到起始位置
fputs("acccsffe\n",pf);
int c1 = ftell(pf);
printf("%d\n",c1);
fclose(pf);
pf = NULL;
return 0;
}
?
完~
?