文章目录
- 1. 什么是文件
- 1.1为什么有文件?
- 1.2什么是文件
- 1.3文件的分类
- 1.4文件缓冲区
- 2.文件的打开与关闭
- 2.1文件的打开(fopen)
- 2.2文件的关闭(fclose)
- 3.顺序读写数据文件
- 3.1读写字符
- 3.2读写字符串
- 3.3格式化读写
- 3.4二进制读写
- 4.文件的随机读写
- 4.1fseek
- 4.2ftell
- 4.3rewind
- 5.文件读取结束的判定与出错检测
- 5.1结束判定
- 5.2出错检测
1. 什么是文件
1.1为什么有文件?
在之前的学习中,我们把数据的输入和输出都是以终端为对象的,即从终端的键盘输入数据,运行结果输出到终端显示器上的。
实际上,有一些数据是需要保存起来的,这里就要使用到文件了。
1.2什么是文件
所谓文件,一般指存储在外部介质上(硬盘…)数据的集合。
文件有不同的类型,我们主要学习两种文件:
- 程序文件
程序⽂件包括源程序⽂件(后缀为.c),⽬标⽂件(windows环境后缀为.obj),可执⾏程序(windows 环境后缀为.exe)。
- 数据文件
⽂件的内容不⼀定是程序,⽽是程序运⾏时读写的数据,⽐如程序运⾏需要从中读取数据的⽂件,或者输出内容的⽂件。
1.3文件的分类
根据数据的组织形式,数据文件被称为⽂本⽂件
或者⼆进制⽂件
。
- 数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存,就是⼆进制⽂件。
- 如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的⽂件就是⽂本⽂件。
那么一个数据在磁盘上是怎么存储的呢?
- 字符⼀律以ASCII形式存储,
- 数值型数据既可以⽤ASCII形式存储,也可以使⽤⼆进制形式存储。
如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占⽤5个字节(每个字符⼀个字节),⽽⼆进制形式输出,则在磁盘上只占4个字节。
1.4文件缓冲区
ANSIC 标准采⽤“缓冲⽂件系统”处理的数据⽂件的,所谓缓冲⽂件系统是指系统⾃动地在内存中为程序中每⼀个正在使⽤的⽂件开辟⼀块“⽂件缓冲区”。
从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才⼀起送到磁盘上
。如果从磁盘向计算机读⼊数据,则从磁盘⽂件中读取数据输⼊到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的⼤⼩根据C编译系统决定的。
简而言之,向(从)磁盘输入(输出)数据,必须先经过缓冲区,你缓冲区放满了,操作系统才会搭理你。
2.文件的打开与关闭
在学习文件的打开与关闭前,我们先了解一个概念叫文件指针。
缓冲⽂件系统中,关键的概念是“⽂件类型指针”,简称“⽂件指针”。
每个被使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息(如⽂件的名字,⽂件状态及⽂件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名FILE.
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
FILE* pf // 文件指针变量
定义pf是⼀个指向FILE类型数据的指针变量。可以使pf指向某个⽂件的⽂件信息区(是⼀个结构体变量)。通过该⽂件信息区中的信息就能够访问该⽂件。也就是说,通过⽂件指针变量能够间接找到与它关联的⽂件
。
2.1文件的打开(fopen)
所谓“打开”,是指为文件建立相应的信息区(用来存放有关文件的信息)和文件缓冲区。
C语言规定,使用fopen函数打开文件。
- 该函数有两个参数,第一个参数是文件的名字,第二个参数是文件的打开方式。
- 文件打开成功,返回一个指向该文件的文件指针
- 文件打开失败,返回一个空指针。
2.2文件的关闭(fclose)
所谓“关闭”,是指撤销文件信息区和文件缓冲区,使文件指针变量不再指向该文件,显然就无法进行文件的读写了。
注意:fclose关闭文件时,会先把缓冲区中的数据输出到磁盘文件中,然后才撤销文件信息区。
- 如果文件
关闭成功,则返回0
- 如果文件
关闭失败,返回EOF(-1)
- 该函数并不会将文件指针置为NULL,因此需要使用则置为NULL。
int main()
{
FILE* pf = fopen("text.txt", "r"); //打开文件
if (pf == NULL)
{
printf("cannoy open this file\n");
return 1;
}
fclose(pf); //关闭文件
pf = NULL;
return 0;
}
3.顺序读写数据文件
3.1读写字符
函数名 | 调用形式 | 功能 | 返回值 | 适用于 |
---|---|---|---|---|
fgetc | fgetc(fp) | 从fp所指向的文件中读取一个字符 | 读取成功,返回所读取的字符;读取失败,返回EOF | 所有输入流 |
fputc | fputc(ch,fp) | 将字符ch写到文件指针变量所指向的文件中 | 写入成功,返回所写入的字符;写入失败,返回EOF | 所有输出流 |
- fputc
- fgetc
- 写一段程序,将source.txt中的内容复制到dest.txt中
#include<stdio.h>
int main()
{
FILE* read = fopen("source.txt", "r");
FILE* write = fopen("dest.txt", "w");
if (read == NULL)
{
printf("read cannot open this file\n");
return 1;
}
if (write == NULL)
{
//若写时打开文件失败,那么因该把读的文件也关闭掉
fclose(read);
read = NULL;
printf("write cannot open this file\n");
return 1;
}
char ch = 0;
while ((ch = fgetc(read)) != EOF)
{
fputc(ch, write);
}
fclose(read);
read = NULL;
fclose(write);
write = NULL;
return 0;
}
3.2读写字符串
函数名 | 调用形式 | 功能 | 返回值 | 适用于 |
---|---|---|---|---|
fgets | fgets(str,n,fp) | 从fp所指向的文件中读取一个长度为(n-1) 的字符串,存放到字符数组str中 | 读取成功,返回str的地址;读取失败,返回NULL | 所有输入流 |
fputs | fputs(str,fp) | 将str所指向的字符串写到文件指针变量所指向的文件中 | 写入成功,返回非负数;写入失败,返回EOF | 所有输出流 |
fgets函数有几点需要注意:
- 它读取的字符串的长度为n-1,并在最后加上一个‘\0’字符,然后把这n个字符存到字符数组中。
- 如果在读完n-1个字符之前遇到‘\n’或文件结束标志EOF,读入即结束,但会将遇到的‘\n’也作为一个字符读入。
fputs注意事项:
- 字符串末尾的’\0’不输出。
3.3格式化读写
函数名 | 调用形式 | 功能 | 返回值 | 适用于 |
---|---|---|---|---|
fscanf | fscanf(fp,格式字符串,输出表列) | 从fp所指向的文件中按照格式字符串读取数据 | 成功后,该函数返回已成功填充的参数列表的项数;读取失败,返回EOF | 所有输入流 |
fprintf | fprintf(fp,格式字符串,输出表列) | 从fp所指向的文件中按照格式字符串写数据 | 成功后,将返回写入的字符总数;写入失败,返回负数 | 所有输出流 |
struct s
{
char name[20];
int age;
float score;
};
void write()
{
struct s s1 = { "zhangsan", 20, 66.0f };
FILE* pf = fopen("text.txt", "w");
int ret = fprintf(pf, "%s %d %f", s1.name, s1.age, s1.score);
printf("%d\n", ret);//写入字符总数
fclose(pf);
pf = NULL;
}
void read()
{
struct s s2 = { 0 };
FILE* pf = fopen("text.txt", "r");
int ret = fscanf(pf, "%s %d %f", s2.name, &s2.age, &s2.score);
printf("%d\n", ret);//读取了参数列表中的几项
//fprintf(stdout, "%s %d %f", s2.name, s2.age, s2.score);
fclose(pf);
pf = NULL;
}
int main()
{
write();
read();
return 0;
}
3.4二进制读写
两个函数仅适用于文件输入、输出流
- fread
- 该函数4个参数
- ptr:指向大小至少为 (size*count) 字节的内存块的指针
- size:要读取的每个元素的大小(以字节为单位)
- count:元素的数量,每个元素的大小为 bytes
- stream:指向指定输入流的 FILE 对象的指针
返回成功读取的元素总数。
如果此数字与 count 参数不同,则表示读取时发生读取错误或已达到文件末尾。
- fwrite
- 该函数也是4个参数
- ptr:指向要写入的元素数组的指针
- size:要写入的每个元素的大小(以字节为单位)
- count:元素的数量,每个元素的大小为 bytes
- stream:指向指定输出流的 FILE 对象的指针
4.文件的随机读写
4.1fseek
根据⽂件指针的位置和偏移量来定位⽂件指针。
三个参数:
- 文件类型的指针
- 相对于起始点的偏移量
- 起始点(已规定好),如下图
4.2ftell
返回⽂件指针相对于起始位置的偏移量。
有时候我们使用完fseek函数后,就不知道文件指针在什么位置了,因此可以借助fteel函数找出文件指针相对于起始位置的偏移量。
4.3rewind
让⽂件指针的位置回到⽂件的起始位置。
int main()
{
int n;
FILE* pFile;
char buffer[27];
pFile = fopen("myfile.txt", "w+");
//向文件中写。文件指针后移
for (n = 'A'; n <= 'Z'; n++)
fputc(n, pFile);
//使文件指针回到起始位置
rewind(pFile);
//从文件指针位置开始读取
fread(buffer, 1, 26, pFile);
fclose(pFile);
buffer[26] = '\0';
printf(buffer);
return 0;
}
5.文件读取结束的判定与出错检测
5.1结束判定
在⽂件读取过程中,不能⽤feof函数的返回值直接来判断⽂件的是否结束。
feof 的作用是:当⽂件读取结束的时候,判断读取结束的原因是否是:遇到文件尾结束
。
- ⽂本⽂件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
- fgetc 判断是否为 EOF
- fgets 判断返回值是否为 NULL
- ⼆进制⽂件的读取结束判断,判断返回值是否小于实际要读的个数。
fread函数会返回所读取的元素总数。
如果总数不相等,则需要判断是到了文件末尾(feof),还是读取错误(ferror)。
5.2出错检测
ferroe返回值为0,表示未出错,如果返货一个非0值,则表示出错。