(零)引入
终端是计算机系统中与用户进行交互的界面。
在以往的程序中,我们通过终端用键盘输入数据,通过屏幕输出信息。
但是,如果我们不想手动低效地输入数据,而是通过文件一次性高效输入;
如果我们不想让数据丢失,而是把输出的数据存储到文件中时,
就需要使用一种新的操作——文件操作。
零.1什么是文件?
文件是计算机系统中的一种数据存储形式;
如文本、图像、音频、视频等。
(一)文件
1.1为什么使用文件?
程序运行时,我们写的程序的数据 是存储在电脑的内存中;如果程序退出,内存会被操作系统回收,于是数据就丢失了;等再次运行程序,无法得到上次程序的数据的。
如果要将数据进行持久化的保存,就要使用文件。
1.2文件的特点:
文件可以由应用程序创建、读取、编辑、删除,它们是计算机系统中存储和传输数据的基本单位;
文件可以被命名并存储在计算机的存储介质中;
文件还可以根据其格式和扩展名来区分不同类型的文件, 如.txt文件是纯文本文件。
1.2.1文件名
文件名是用来标识和区分文件的名称。它是文件系统中文件的唯一标识符。
文件名的作用是以便用户识别和引用。
文件名包含3部分:文件路径+文件名主干+文件后缀
例如: c:\code\test.txt
重点:在C源文件中引用文件名要注意用两个斜杠来转义为一个斜杠
转义字符:" \\ == \ "
1.3文件的分类
但是在程序设计中,我们⼀般谈的文件有两种:程序文件、数据文件。
1.3.1程序文件
程序文件是包含计算机程序代码的文件。
例如:".c"文件表示C语言程序文件, ".java"文件表示Java程序文件, ".py"文件表示Python程序文件等等。
但是,程序文件不是我们要深入讨论的文件,因为我们是在程序文件中写代码,而进行文件操作需要的文件不是程序文件,而是数据文件。
1.3.2数据文件
文件的内容不⼀定是程序代码,也可以是程序运行时读写的数据,比如一些文件在程序运行时被程序读取,同时接收并存储程序输出的数据 —— 这些提供输入数据和接受输出数据的文件就是数据文件。
我们之前 所处理数据的输入输出都是以终端为对象的,即从终端的键盘输⼊数据,运行结果从终端显示到显示器(屏幕)上。
其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件,这就是使用文件的意义。
1.3.2.1 二进制文件和文本文件?
根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存,就是二进制文件。通常情况下,我们无法阅读二进制文件
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的⽂件就是文本文件。
如何形象理解二进制文件与文本文件呢?
假如我们将10000 , ‘a’ 等存入内存中,那么他在内存中是怎么存储的呢?
对于字符,由于有ASCII表的存在,无论是二进制形式,还是文本形式,都是一样的;
对于数字,就有所不同了,以10000为例,看一看他在内存中是怎么存储的呢?
例子:
当以ASCII形式:每一个位都需要一个字节,需要5个字节;
当以二进制形式:10000默认是整形,只需要4个字节。
数字在内存中的存储还是有很大不同的。
到这里,我们理一下思路——>
小结:
(二)文件的打开和关闭
为了更好的讲解文件操作,我们先要引入“流”的概念;
2.1什么是“流”
我们的程序存在的意义就是处理问题,也就是说:需要程序从外部获取信息,经过处理之后输出信息。但是不同的设备输入信息的渠道是不同的,为了方便程序从外部获取信息,并且输出信息,C语言抽象出流的概念。
C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。
⼀般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作,之后再关闭流。
2.1.1标准流
那为什么我们从键盘输⼊数据,向屏幕上输出数据,并没有打开流呢?
那是因为C语言程序在启动的时候,默认打开了3个流:
• stdin-标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。
• stdout-标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出流中。————————标准输入输出流,说白了就是键盘和屏幕。
• stderr-标准错误流,⼤多数环境中输出到显⽰器界⾯。
默认打开这三个流后,我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的。
stdin、stdout、stderr三个流的类型是: FILE* ,通常称为⽂件指针。
C语⾔中,就是通过 FILE* 的⽂件指针来维护流的各种操作的。
2.2文件指针
每个被使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息(如⽂件的名字,⽂件状态及⽂件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名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结构的变量,并填充其中的信息,使⽤者不必关⼼细节。
这个结构体储存了文件的关键信息,我们通过对结构体内对象的操作,间接实现对文件的操作:
2.3文件的打开和关闭
对文件在读写之前应该先打开⽂件,在使⽤结束之后应该关闭⽂件。
使用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+”是复合的操作,是同时能够读和写的打开方式。
2.4文件的读写
文件的读写有两种类型:顺序读写与随机读写
对于顺序读写,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;
}
2.5⽂件的随机读写
随机读写,实际意义是我们可以控制读写的位置,而不是真的“随机”。
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;
}
完~