网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
读写操作包括了文件的顺序读写 和随机读写 以及各种常用的函数讲解
1.文件的顺序读写
文件顺序读写表示从文件信息区起始位置开始往文件信息区里读出或者写入数据
文件信息区起始地址就相当于是文件指针偏移量为0时所指向的位置
下面是各种常用的向文件信息区里输入或者输出数据的库函数功能 函数名和适用的流
功能 | 函数名 | 适用于 |
---|---|---|
字符输入函数 | fgetc | 所有输入流 |
字符输出函数 | fputc | 所有输出流 |
文本行输入函数 | fgets | 所有输入流 |
文本行输出函数 | fputs | 所有输出流 |
格式化输入函数 | fscanf | 所有输入流 |
格式化输出函数 | fprintf | 所有输出流 |
二进制输入 | fread | 文件 |
二进制输出 | fwrite | 文件 |
①.输入流和输出流的概念
输入输出流可以看做往从文件里读取数据的过程和将数据输出到文件的过程
流看作一个过程,也可以相当于一个通道
适用于所有输入流:表示任何文件或其他可以是输入数据的程序文件或磁盘文件都可以往程序里输入数据,拿文件和键盘来说 ,键盘相当于是标准输入流,所有输入流表示可以从文件里读取数据,也可以从键盘上读取数据
键盘实际也相当于是一个文件,文件名为stdin,在c程序使用时默认已经打开了此文件
适用于所有输出流:表示可以将数据输出到任何文件里或者可以保存数据的程序里, 拿普通文件和屏幕来说,屏幕也被称为标准输出流. 所有输出流表示可以将数据输出到文件,也可以输出到屏幕上
屏幕实际上也可以看成一个文件,文件名为stdout,在c程序打开时默认打开了屏幕文件
而在文章前部分提到了了stdin 和stdout和stderr ,stderr表示的是将错误信息输出到屏幕上 ,stdin stdou stderr都可以看做是程序一开始就已经默认打开的文件,都可以直接使用
适用于文件:一般表示只能将数据输出到普通文件上,常用于将数据以二进制形式输出到文件中,因为二进制形式是计算机读取的语言,所以不适用于所有输入输出流,一般保存在文件里,并且对于人来说可读性差.
②.fgetc和fputc字符输入输出函数
fgetc字符输入函数, 返回类型为int 形参类型为FILE*文件指针
表示从文件信息区里文件指针当前偏移量处读取一个字符将这个字符的ASCLL码返回,并将文件指针偏移量往后移动一位
如果未读取到字符则会返回EOF(一般值为-1)
fputc字符输出函数, 返回类型int 函数参数 int 和FILE*
表示将一个整数以ASCLL码字符形式输出到文件指针指向的文件信息区的当前偏移量处,返回值是输出的字符 如果输出发生了错误则会返回EOF
#define \_CRT\_SECURE\_NO\_WARNINGS
#include<stdio.h>
int main()
{
FILE\* pf = fopen("test.txt", "w");//以只写形式打开
if (pf == NULL)
{
printf("打开失败\n");
perror("fopen");
}
char c ;
int n=fputc('A', pf); // 将字符A输出到pf指向的文件信息区
if (n == EOF)
{
printf("输出字符失败\n");
}
fclose(pf); //将pf文件指针指向的文件信息区关闭
pf = NULL;
pf = fopen("test.txt", "r"); //以只读形式打开
if (pf == NULL)
{
printf("打开失败\n");
perror("fopen");
}
n=fgetc(pf);
if (n == EOF)
{
printf("读取字符失败\n");
}
else
{
printf("%c", n);
}
fclose(pf); //将pf文件指针指向的文件信息区关闭
pf = NULL; //将pf指针变量 里置为NULL
return 0;
}
上面是fputc和fgetc的测试代码,先以只写形式打开test.txt文件
往文件信息区里输入字符A,然后再关闭此文件信息区,再以只读形式打开test.txt文件 从文件信息区里读取第一个字符,则读取了前面输入的字符A
为什么要关闭文件再打开而不直接读取?
1.因为设置了是只写形式打开文件只能写不能读
2.fputc输出一个字符到文件信息区里此时文件指针偏移量会往后移动一个位置表示此时指向文件里面A字符后面的一个位置,此时直接读取是读取不到字符A的需要使用fewind函数重置文件偏移量或者重新打开文件刷新偏移量
③.fgets和fputs文本行输入输出函数
fgets第一个参数为数组名用来接受读取的字符的数组
第二个为要读取的字符串字符个数,
第三个为文件指针 表示从文件里读取num-1个字符
num是第二个参数,表示读取num-1个字符然后最后自动加上\0字符得到num个字符赋给第一个参数
在读取过程中如果遇到\n会读取掉\n然后直接结束 读最后将读取到的字符串最后加上\0放在指定数组中(数组容量要大)
读取成功会返回存放读取的字符串的字符指针,读取失败返回NULL
fputs文本行输出函数, 将字符串输出到文件指针指向的文件信息区,文件指针偏移量再往后移动输出的字符个数的大小
输出成功返回非负值,失败返回EOF
#define \_CRT\_SECURE\_NO\_WARNINGS
#include<stdio.h>
int main()
{
FILE\* pf = fopen("text.txt", "w");
if (pf == NULL)
{
perror("fopen");
}
else
{
int n=fputs("123\n456", pf); //将123456输出到pf指向的文件信息区
if (n == EOF) //如果返回值为EOF则表示输出失败
{
printf("输出失败\n");
}
else //否则输出成功
{
printf("输出成功\n");
}
fclose(pf); //使用完后将文件信息区关闭
pf = NULL;
}
pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
}
else
{
char a[7];
char\* tmp = fgets(a, 6, pf); //从pf指向的文件信息区读取 3个字符后加上\0共4个字符如果中途遇到\n则会直接读取\n后结束读取将读取的字符串在最后加上\n然后 存放在a数组中
if (tmp == NULL)
{
printf("读取信息失败\n");
}
else
{
printf("已读取信息:%s", a);
}
}
return 0;
}
④fscanf和fprintf格式化输入输出函数
这两个函数和scanf printf差不多 多的就是一个文件指针
fscanf 比scanf最左边多了个参数,表示文件指针,其它的参数和scanf一样,
表示从文件指针指向的文件信息区中读取对应格式的数据输入到对应的变量里
最后返回的值是赋值的变量个数,未分配则返回EOF
fprintf()函数根据指定的format(格式)(格式)发送信息(参数)到由stream(流)指定的文件. fprintf()只能和printf()一样工作. fprintf()的返回值是输出的字符数,发生错误时返回一个负值.
表示将对应变量里的值以对应格式输出到文件指针指向的信息区内,最后返回输出的字符个数,发生错误则返回复制
#define \_CRT\_SECURE\_NO\_WARNINGS
#include<stdio.h>
int main()
{
FILE\* pf = fopen("text.txt", "w");
if (pf == NULL)
{
perror("fopen");
}
else
{
int n=fprintf(pf, "%s", "Hello World");
if (n < 0)
{
printf("输出失败\n");
}
else
{
printf("输出成功\n");
}
fclose(pf);
pf = NULL;
}
pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
}
else
{
char arr[20];
fscanf(pf, "%s", arr); //和scanf读取效果一样 遇到间隔符后结束读取 偏移量移到读取结束的位置
printf("读取的内容为:%s\n", arr); // 读取到Hello 遇到空格结束将读取到的字符给arr
fscanf(pf, "%s\n", arr); //从空格位置开始读取跳过空格将后面的World读取到末尾结束将字符串给arr
printf("读取的内容为:%s\n", arr);
}
return 0;
}
⑤.fread和fwrite二进制输入输出函数
fread和fwrite 都是以二进制形式输入输出,输入到文件里显示的数据是二进制形式,具有一定的数据安全功能,因为很多人是读不懂二进制数的
函数fread()读取[num]个对象(每个对象大小为size(大小)指定的字节数),并把它们替换到由buffer(缓冲区)指定的数组. 数据来自给出的输入流. 函数的返回值是读取的内容数量…
使用feof()或ferror()判断到底发生哪个错误
fwrite()函数从数组buffer(缓冲区)中, 写count个大小为size(大小)的对象到stream(流)指定的流. 返回值是已写的对象的数量.
#define \_CRT\_SECURE\_NO\_WARNINGS
#include<stdio.h>
int main()
{
FILE\* pf = fopen("text.txt", "wb"); //以二进制只写形式打开文件
if (pf == NULL)
{
perror("fopen");
}
else
{
int a=666666;
int n=fwrite(&a, sizeof(a), 1, pf); //从 a里 读取1 个 sizeof(a)大小 的数据转换为二进制形式输出到pf指向的文件信息区中
if (n != 1)
{
printf("输出失败\n");
}
else
{
printf("输出成功\n");
fclose(pf);
pf = NULL;
}
}
pf = fopen("text.txt", "rb");//以二进制只读形式打开文件
if (pf == NULL)
{
perror("fopen");
}
else
{
int num;
fread(&num, sizeof(num), 1, pf); //从文件信息中中读取 1个 sizeof(num)个大小的数据 放到num中
printf("读取的数据为:%d", num);
}
return 0;
}
从输出结果可以看出 以二进制形式将整形变量里的数输出到文件里,在文件里显示的数据是乱码,但只是在我们视角是乱码,在计算机内部表示的是666666 ,在最后以二进制形式读取到变量n中最后结果是666666
2.文件的随机读写
文件的随机读写,也是站在文件指针偏移量的角度来看,可以改变文件指针偏移量读取文件信息区中任意位置的数据
①fseek定位文件指针偏移量函数
函数fseek()为给出的流设置位置数据. origin的值应该是下列值其中之一(在stdio.h中定义):
SEEK_SET可以用数字0表示
SEEK_CUR可以用数字1表示
SEEK_END可以用数字2表示
fseek()成功时返回0,失败时返回非零. 你可以使用fseek()移动超过一个文件,但是不能在开始处之前. 使用fseek()清除关联到流的EOF标记.
fseek函数主要作用就是可以时当前指针偏移量变为从偏移量0位置移动n各单位 或者从当前偏移量位置移动n个单位 或者从文件末尾移动n个单位
#define \_CRT\_SECURE\_NO\_WARNINGS
#include<stdio.h>
int main()
{
FILE\* pf = fopen("text.txt", "w+"); //以读和写形式打开文件
if (pf == NULL)
{
perror("fopen");
}
else
{
fputc('a', pf);
fputc('b', pf);
fputc('c', pf);
char a = fgetc(pf);
printf("%c", a);//输出结果是什么?
}
return 0;
}
运行三个fputc表示将abc输出到文件中,每输出一个字符文件指针偏移量往后移动一个位置,输出完c后文件指针偏移量移动到c后面,此时直接用fgetc读取的是文件指针偏移量处的字符,而此时偏移量处没有字符所以什么都没读取
此时可以用到fseek函数改变文件指针偏移量
将文件指针偏移量往左移动一个单位此时就到了指向c的位置,此时即可读取字符c
此时将文件指针偏移量从起始0位置往右移动1个位置就是b的位置,此时就可以读取b
此时表示从文件末尾往左移动-1个单位,文件末尾就是字符c后面的位置移动-1个单位就是c的位置 此时可以读取字符c
②.ftell获取当前文件指针偏移量函数
ftell函数 里面放文件指针,可以得到当前文件指针的偏移量并返回,即相对于起始地址偏移了多少个单位
可以看到经过最后读取完字符c后指针偏移量加1为了3,此时获取的文件指针偏移量为3
③.rewind重置文件指针偏移量函数
函数rewind()把文件指针移到由stream(流)指定的开始处, 同时清除和流相关的错误和EOF标记.
不管文件指针偏移量是多少,使用此函数后会将指针偏移量变为0
可以看到使用了rewind函数后 文件指针偏移量为0了
五.文本文件和二进制文件
根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件
#include <stdio.h>
int main()
{
int a = 10000;
FILE\* pf = fopen("test.txt", "wb");
fwrite(&a, 4, 1, pf);//二进制的形式写到文件中
fclose(pf);
pf = NULL;
return 0;
}
执行这串代码后在源程序目录里多出了text.txt文件 ,而此时里面的看不懂的字符,这正是二进制数据,通过二进制形式输出到文件中
vs编辑器里有以二进制形式打开文件的方法↓
此时用二进制形式打开文件后可以看出这个二进制文件表达的含义
10 27 00 00 表示是在内存中的一串二进制数据以小端形式显示(数据低位放低地址,高位放高地址)转换为十进制数就是10000
六.文件读取结束的判定
在进行读取文件数据结束时可能有两种情况:
1.读取到文件结束标准EOF结束读取
2.读取文件过程中发生错误结束读取
要区分实际读取结束是哪种情况需要feof和ferror函数判定
1.feof判断文件读取末尾结束函数
函数feof()在到达给出的文件流的文件尾时返回一个非零值.
当读取结束后可以用此函数可以判断文件指针是否在文件末尾结束,如果是返回非零值 如果不是返回零值
2.ferror判断文件读取失败结束函数
ferror()函数检查stream(流)中的错误, 如果没发生错误返回0,否则返回非零. 如果发生错误, 使用perror()检测发生什么错误.
#include <stdio.h>
int main()
{
int c; // 注意:int,非char,要求处理EOF
FILE\* fp = fopen("test.txt", "r");
if (fp==NULL) {
perror("File opening failed");
}
//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading"); //读取文件过程中失败
else if (feof(fp))
puts("End of file reached successfully"); //成功读取到文件末尾
fclose(fp);
}
使用fgetc读取文件中123456时 读取过程失败或者读取到文件末尾结束标志时都会返回EOF
当读取到EOF后结束循环,用feof和ferror分别判断是读取到末尾结束的还是读取失败结束的…
牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。
而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。
- 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets ) 例如: fgetc 判断是否为 EOF . fgets 判断返回值是否为 NULL .
- 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。 例如: fread判断返回值是否小于实际要读的个数
七.了解文件缓冲区概念
ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。
从内存向磁盘输出数据会先送到内存中的缓冲区, 装满缓冲区后才一起送到磁盘上。
如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),
然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。
缓冲区的大小根据C编译系统决定的。
文件缓冲区的使用就类似于老师上课过程中,不断有学生问问题,老师为了上课进度,不可能每有一个问题就停下来回答,而是会将问题留到下课后一一解答
就好比缓存区,程序在运行中输入输出的数据都会先放到缓冲区中,非必要情况下,都是得缓冲区满或者程序结束前关闭 文件后才将数据输入输出到程序中,这样可以节省提高性能
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
个正在使用的文件开辟一块“文件缓冲区”。
从内存向磁盘输出数据会先送到内存中的缓冲区, 装满缓冲区后才一起送到磁盘上。
如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),
然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。
缓冲区的大小根据C编译系统决定的。
文件缓冲区的使用就类似于老师上课过程中,不断有学生问问题,老师为了上课进度,不可能每有一个问题就停下来回答,而是会将问题留到下课后一一解答
就好比缓存区,程序在运行中输入输出的数据都会先放到缓冲区中,非必要情况下,都是得缓冲区满或者程序结束前关闭 文件后才将数据输入输出到程序中,这样可以节省提高性能
[外链图片转存中…(img-YR6JWcmT-1715294203849)]
[外链图片转存中…(img-LlJ4IbWe-1715294203849)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!