『C语言进阶』文件操作(一):https://developer.aliyun.com/article/139270
4. fgets (从流中获取字符串)
char* fgets(char* str,int num,FILE* stream);
参数:
str:指向在其中复制字符串读取的字符数组的指针。
num:要复制到到 str 的最大字符数(包括终止空字符)。
stream:指向标识输入流的 FILE 对象的指针。(stdin可以用作从标准输入读取的参数)
#include<stdio.h> int main() { FILE* pf = fopen("date.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } char buf[10] = { 0 }; fgets(buf, 10, pf); printf("%s\n", buf); fgets(buf, 10, pf); printf("%s\n", buf); fclose(pf); pf = NULL; return 0; }
注意:
从流中读取字符并将其作为 C 字符串存储到 str 中,直到读取 (num-1) 个字符或到达换行符或文件末尾。
例题:
实现一个代码,将date.txt拷贝一份生成date2.txt
#include<stdio.h> int main() { FILE* pr = fopen("date.txt", "r"); if (pr == NULL) { perror("fopen"); return 0; } FILE* pw = fopen("date2.txt", "w"); if (pw == NULL) { perror("fopen"); fclose(pr); pr = NULL; return 0; } int ch = 0; while ((ch = fgetc(pr)) != EOF) { fputc(ch, pw); } fclose(pw); pw = NULL; return 0; }
5. fprintf (将格式化数据写入流)
int fprintf ( FILE * stream, const char * format, … );
我们对比printf和fprintf,发现fprintf比printf多一个流,所以我们使用fprintf打印的格式加一个流
#include<stdio.h> struct Stu { char name[20]; int age; double d; }; int main() { struct Stu s = { "zhangsan",20,98.2 }; FILE* pf = fopen("date.txt", "w"); if (pf == NULL) { perror("fopen"); return 0; } fprintf(pf, "%s %d %lf", s.name, s.age, s.d); fprintf(stdout, "%s %d %lf\n", s.name, s.age, s.d); printf("%s %d %lf", s.name, s.age, s.d); fclose(pf); pf = NULL; return 0; }
6. fscanf (从流中读取格式化数据)
int fscanf ( FILE * stream, const char * format, … );
#include<stdio.h> #include<string.h> struct Stu { char name[20]; int age; double d; }; int main() { struct Stu s = { 0 }; FILE* pf = fopen("date.txt", "r"); if (pf == NULL) { perror("fopen"); return 0; } fscanf(pf, "%s %d %lf", s.name, &(s.age), &(s.d)); printf("%s %d %lf", s.name, s.age, s.d); fclose(pf); pf = NULL; return 0; }
scanf 从标准输入流读格式化的数据
printf 向标准输出流写格式化的数据
**fscanf 适用于所有输入流的格式化输入函数 **
**fprintf 适用于所有输出流的格式化输出函数 **
**sscanf 从字符串中读取格式化的数据 **
sprintf 将格式化的数据转化成字符串
7. fwrite (写入要流式传输的数据块)
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
参数:
ptr:指向要写入的元素数组的指针,转换为 const void*。
size:要写入的每个元素的大小(以字节为单位)。
count:元素数,每个元素的大小为字节大小。
#include<stdio.h> #include<string.h> struct Stu { char name[20]; int age; double d; }; int main() { struct Stu s[2] = { {"zhangsan",20,78.9},{"lisi",18,38.4} }; FILE* pf = fopen("date.txt", "wb"); if (pf == NULL) { perror("fopen"); return 0; } //按照二进制的方式写文件,放进二进制信息 fwrite(s, sizeof(struct Stu), 2, pf); fclose(pf); pf = NULL; return 0; }
8. fread (从流中读取数据块)
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
参数:
ptr:指向大小至少为 (sizecount) 字节的内存块的指针,转换为 void*。
size:要读取的每个元素的大小(以字节为单位)。
count:元素数,每个元素的大小为字节大小。
#include<stdio.h> #include<string.h> struct Stu { char name[20]; int age; double d; }; int main() { struct Stu s[2] = {0}; FILE* pf = fopen("date.txt", "rb"); if (pf == NULL) { perror("fopen"); return 0; } //按照二进制的方式读文件 fread(s, sizeof(struct Stu), 2, pf); printf("%s %d %lf", s[0].name, s[0].age, s[0].d); printf("%s %d %lf", s[1].name, s[1].age, s[1].d); fclose(pf); pf = NULL; return 0; }
知识点:
fread的返回值是读取文件内容的块数目,不是文件总大小
总结:
scanf从标准输入流(stdin)上进行格式化输入的函数
printf向标准输出流(stdout)上进行格式化的输出函数
fscanf可以从标准输入流(stdin)/指定的文件上读取格式化的数据
fprintf把数据按照格式化的方式输出到标准输出流(stdout)/指定的文件
sscanf可以从一个字符串提取(转化)出格式化数据
sprintf把一个格式化的数据转化为字符串
四、文件的随机读写
4.1 fseek (根据文件指针的位置和偏移量来定位文件指针)
int fseek ( FILE * stream, long int offset, int origin );
参数:
offset:二进制文件:要从源偏移的字节数。
文本文件:零或ftell返回的值。
origin:用作偏移参考的位置。
SEEK_SET | 文件开头 |
SEEK_CUR | 文件指针的当前位置 |
SEEK_END | 文件结尾 |
#include<stdio.h> int main() { FILE* pf = fopen("date.txt", "r"); if (pf == NULL) { perror("fopen"); return 0; } fseek(pf, 5, SEEK_SET); int ch = fgetc(pf); printf("%c\n", ch); fclose(pf); pf = NULL; return 0; }
#include<stdio.h> int main() { FILE* pf = fopen("date.txt", "r"); if (pf == NULL) { perror("fopen"); return 0; } fseek(pf, -9, SEEK_END); int ch = fgetc(pf); printf("%c\n", ch); fclose(pf); pf = NULL; return 0; }
4.2 ftell (返回文件指针相对于起始位置的偏移量)
long int ftell ( FILE * stream );
#include<stdio.h> #include<string.h> int main() { FILE* pf = fopen("date.txt", "r"); if (pf == NULL) { perror("fopen"); return 0; } int ch = fgetc(pf); printf("%c\n", ch); ch = fgetc(pf); printf("%c\n", ch); ch = fgetc(pf); printf("%c\n", ch); int ret = ftell(pf); printf("%d\n", ret); fclose(pf); pf = NULL; return 0; }
运行结果:
z h a 3
4.3 rewind (让文件指针的位置回到文件的起始位置)
void rewind ( FILE * stream );
#include<stdio.h> #include<string.h> int main() { FILE* pf = fopen("date.txt", "r"); if (pf == NULL) { perror("fopen"); return 0; } int ch = fgetc(pf); printf("%c\n", ch); ch = fgetc(pf); printf("%c\n", ch); ch = fgetc(pf); printf("%c\n", ch); rewind(pf); ch = fgetc(pf); printf("%c\n", ch); fclose(pf); pf = NULL; return 0; }
运行结果:
z h a z
五、文本文件和二进制文件
根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。
简单来讲,我们看不懂的是二进制文件,看得懂的就是文本文件。
一个数据在内存中是怎么存储的呢?
字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。
例如:有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而 二进制形式输出,则在磁盘上只占4个字节。
六、文件读取结束的判断
在文件读取过程中,不能用feof函数的返回值直接来判断文件的是否结束。
feof 的作用是:当文件读取结束的时候,判断是读取结束的原因是否是:遇到文件尾结束。
- 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
例如:
- fgetc 判断是否为 EOF .
- fgets 判断返回值是否为 NULL .
- 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如:
- fread判断返回值是否小于实际要读的个数。
#include <stdio.h> #include <stdlib.h> int main() { int c; // 注意:int,非char,要求处理EOF FILE* fp = fopen("test.txt", "r"); if (!fp) { perror("File opening failed"); return EXIT_FAILURE; } //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); fp == NULL; return 0; }
七、文件缓冲区
ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序 中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装 满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓 冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根 据C编译系统决定的。
本次的内容到这里就结束啦。希望大家阅读完可以有所收获,同时也感谢各位铁汁们的支持。文章有任何问题可以在评论区留言,小羊一定认真修改,写出更好的文章~~