1.二进制读写函数
在上一章我们介绍了字符读写函数、文本读写函数和格式化输入输出函数,这张我们继续为大家介绍剩下的一组读写函数——二进制读写函数:fread函数和fwrite函数。
⚀fread函数
🟡函数作用
以二进制的方式从指定流中读取数据
🟢函数定义
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
- void* ptr 存放读取数据的空间的起始地址
- size_t size 读取元素的大小,单位为字节
- size_t count 读取元素的个数
- FILE* stream 与指定流关联的文件指针
- size_t 返回值,为读取成功的元素个数
通过函数定义我们再来讲解一下函数的作用:
fread函数是从stream指向流中读取count个size大小的元素,将它们存放在ptr指向的空间里面,并且返回成功读取到的元素个数。
🟣函数使用
#include<stdio.h> int main() { int arr[4]; //1.打开文件 FILE* p = fopen("test2.txt", "rb");//以二进制读的形式打开文件 //"test2.txt"文件中二进制的形式存放着1,2,3,4 //检查文件是否打开成功 if (p == NULL) { perror("fopen");//打印错误信息 return 1;//终止程序 } //2.二进制的方式读取,fread函数 fread(arr, sizeof(arr[0]), 3, p); //存放读入数据的空间的起始地址,每个数据的大小,读入数据的个数,文件指针 //都是以二进制的形式读取,读取为int类型的1是占4个字节,对应32个二进制位,就是将这32个二进制位读取到数组arr中去 fclose(p); p = NULL; for (int i = 0; i < 3; i++) { printf("%d ", arr[i]); } return 0; }
🟤注意事项
- 函数的返回值为成功读取到元素的个数,这个返回值可以和count进行比较,来验证是否全部读取成功。
- fread函数不同于前面的字符、文本读取函数,该函数读取是原始的方式(字节的读取,二进制的读取),指定大小的读取,并非一个字节的字符,一整串字符的读取。
⚁fwrite函数
🟡函数作用
以二进制的形式将数据写入到指定流中
🟢函数定义
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
- void* ptr 存放写入数据的空间的起始地址
- size_t size 写入元素的大小,单位为字节
- size_t count 写入元素的个数
- FILE* stream 与指定流关联的文件指针
- size_t 返回值,为写入成功的元素个数
通过函数定义我们再来讲解一下函数的作用:
fread函数是向stream指向流中写入count个size大小的元素,写入的数据是存放在ptr指向的空间里面,并且返回成功写入到的元素个数。
🟣函数使用
#include<stdio.h> int main() { int arr[4] = { 1,2,3,4 }; //1.打开文件 FILE* p = fopen("test2.txt", "wb");//以二进制写的形式打开文件 //检查文件是否打开成功 if (p == NULL) { perror("fopen");//打印错误信息 return 1;//终止程序 } //2.二进制的方式写入,fwrite函数 fwrite(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), p); //写入数据的空间起始地址,每个数据的大小,写入数据的个数,文件指针 //都是以二进制的形式写入,比如int类型的1是占4个字节,对应32个二进制位,就是将这32个二进制位进行写入 fclose(p); p = NULL; return 0; }
🟤注意事项
- 函数的返回值为成功写入的元素的个数,这个返回值可以和count进行比较,来验证是否全部写入成功。
- fwrite函数是将数据以它原有的形式写入到指定流中,比如int类型的1,是4个字节,原始类型就是31个0,1个1,就是将其二进制的形式写入流中。
2.随机读写操作
在前面我们所学习的读写函数,都是从文件内容的起始位置开始进行操作,且每次使用完,文件指示光标会跳转指定位置(比如字符,操作一个光标向后移动),我们并没有指定到文件内容的位置,那么接着就来学习几个能操作文件的指示光标的函数。
我们先看下这几个函数以及作用:
函数 | 作用 | 含义 |
fseek | 文件的指示光标跳转到指定位置 | seek.寻找 |
ftell | 计算与光标起始位置的偏移量 | tell.计算,数 |
rewind | 文件的指示光标回到起始位置 |
⚀fseek函数
🟢函数定义
int fseek ( FILE * stream, long int offset, int origin );
- FILF* stream 要操作的文件的文件指针
- long int offset 相对于origin的偏移量
- int origin 文件光标的位置,规定只有一下三个值:
SEEK_SET | 文件的开头 |
SEEK_CUR | 文件当前的位置 |
SEEK_END | 文件的末尾 |
- int 函数返回值,如果函数操作成功,返回0;如果操作失败,返回非零数。
🟣函数使用
- 回到文件开头:将origin设置为SEEK_SET,offset设置为0,文件的指示光标就回到起始位置。
- 定位到文件的某个位置:通过origin和offset两者值的配合,光标可以到达任何位置。
- 从当前位置移动:如果想要在当前位置向左/右偏移,将origin设置为SEEK_CUR,将offset设置为要偏移量,左为负,右为正。
- 追加内容:将origin设置为SEEK_END,offset设置为0,文件的指示光标就到了文件的末尾,此时就可进行追加操作。
🟤注意事项
- 函数返回值为0意味着函数操作成功,为非零数意味着操作失败,可用返回值来检查操作。
- fseek通常都是用于以二进制形式打开的流,此时函数的参数可以随意设置。
- 当函数用于文本形式打开的流时,函数的参数就有要求,origin的值只能设置为SEEK_CUR,offset只能设置为0或调用ftell函数返回的值,就意味着对于文本文件调用时只能将文件的指示光标返回到文件的起始位置。
⚁ftell函数
🟢函数定义
long int ftell ( FILE * stream );
- FILE* stream 要计算偏移量的文件的文件指针
- long int 函数返回值,返回的是当前文件的指示光标距离文件开头的距离,单位为 字节;当操作失败时,返回返回长整型的-1,就是-1L。
🟣函数使用
【补充】
- 返回当前位置:ftell函数通过返回一个长整型(long int)值,告知调用者当前文件指针的位置。
- 错误处理:如果发生错误,ftell函数会返回-1L,并且全局变量errno会被设置为一个正值,以指示发生了错误。
- 确定文件大小:结合fseek和ftell的使用,可以方便地计算出文件的大小。例如,先将文件指针移动到文件末尾,然后使用ftell获取当前位置,这个位置就是文件的大小。
- 随机访问文件:在随机存取文件时,由于文件位置频繁前后移动,使用ftell可以轻松确定文件的当前位置。
🟤注意事项
- 对于二进制流(文件以二进制形式打开的),ftell函数得到的是与文件开头的偏移量。
- 对于文本流(文件以普通形式打开的),ftell函数得到的数值可能并无任何意义。
⚂rewind函数
🟢函数定义
void rewind ( FILE * stream );
- FILE* stream 要设置的流
- void 函数值为空,只是一段过程,进行设置文件光标回到文件的开头
🟣函数使用
//假设文件指针p指向的文件的指示光标已经偏移
rewind(p);//光标回到文件的开头
3.文件读写结束的原因
读写结束的原因分为一下两种:
- 读写文件的过程中,到达文件的末尾。
- 读写文件的过程中,文件发生错误。
同样也配有两个函数来检查上面两种情况:
函数 | 作用 | 返回值 |
feof | 检查文件读写过程中是到达文件末尾 | 到达末尾返回0,反之为非零值 |
ferror | 检查文件读写过程中是否出现错误 | 没有错误返回0,反之为非零值 |
除了这两种情况以外,我们还可以通过判断读写函数的返回值,来判断读写是否成功:
函数 | 函数定义 | 函数返回值 |
fgetc | int fgetc ( FILE * stream ) | 成功返回读取到的字符的ASCII码值; 失败返回EOF |
fputc | int fputc ( int character, FILE * stream ) | 成功返回写入的 字符的ASCII码值; 失败返回EOF |
fgets | char * fgets ( char * str, int num, FILE * stream ); | 成功返回str 失败返回NULL |
fputs | int fputs ( const char * str, FILE * stream ); | 成功返回成功写入的字符的个数; 失败返回EOF |
scanf家族 | int 名字( 指定位置, const char * format, ...); | 成功返回成功输入的项数; 失败返回EOF |
printf 家族 | int 名字 ( 指定位置 const char * format, ... ); | 成功返回成功打印的项数; 失败返回负数 |
fread | size_t fread ( void * ptr, size_t size, size_t count, FILE * stream ); | 返回读取到的元素的个数 |
fwrite | size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream ); | 返回成功写入的元素的个数 |
4.文件缓冲区
简单的讲一下:
内存中的数据无法长久保存,想要永久保存,就需要将数据保存到磁盘上,那么内存就需要和磁盘打交道。
当内存中的数据发生改变时,硬盘中的数据也必须跟着改变,如果内存中的数据频繁改变,磁盘中的数据也要跟着频繁的变,那么结果就是内存和磁盘频繁打交道。
频繁的写入就会使得计算机的效率变低,此时就有了缓冲区的概念,数据先不直接磁盘/内存上,而是先放到缓冲区上,当缓冲区的数据被写满之后,再刷新到目标空间上,这就减少频繁的;输出输入,降低了硬件之间的访问频率,提升了效率。
缓冲区的大小是由C语言的编译系统来决定的
本章内容结束,下章见,拜拜!!!