文件操作(二)
1.前言
链接: 文件操作(一)前半部分内容在这里
几个小概念:
标准输入/输出流 | stdin/stdout |
---|---|
所有(指定)输入/输入流 | 文件流;stdin/stdout |
格式化数据 | 就是整型,浮点型那样的数据 |
2.函数对比
2.1.分类说明
scanf
int scanf ( const char * format, … );
Read formatted data from stdin
从标准输入流上读取格式化的数据
fscanf
int fscanf ( FILE * stream, const char * format, … );
Read formatted data from stream
从指定输入流上读取格式化的数据
sscanf
int sscanf ( const char * s, const char * format, …);
Read formatted data from string
在字符串中读取格式化的数据
printf
int printf ( const char * format, … );
Print formatted data to stdout
把数据以格式化的形式打印在标准输出流上
fprintf
int fprintf ( FILE * stream, const char * format, … );
Write formatted data to stream
把数据以格式化的形式打印在指定的输出流上
sprintf
int sprintf ( char * str, const char * format, … );
Write formatted data to string
把格式化的数据转成字符串
2.2.实例说明
对于scanf与printf的使用不过多说明,主要展示后两组函数。
PS:注意包含#define _CRT_SECURE_NO_WARNINGS
不然会有安全警告
案例一:
#include <stdio.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { "张三", 20, 65.5f };
//想把s中的数据存放在文件中
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写文件 - 是以文本的形式写进去的
fprintf(pf, "%s %d %f", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
功能说明:
打开test.txt文件(“w”模式下若文件不存在则新建一个)
使用fprintf向pf写入数据
案例二:
#include <stdio.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { 0 };
//想从文件test.txt中读取数据放在s中
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));
//打印在屏幕上看看
//printf("%s %d %f\n", s.name, s.age, s.score);//
fprintf(stdout, "%s %d %f\n", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
功能说明:
在案例一的基础上用fscanf从test.txt文件中读取数据到s中
用printf/fprintf打印在屏幕上。printf(…)与fprintf(stdin…)并无区别
案例三:
#include <stdio.h>
int main()
{
fputc('a', stdout);
return 0;
}
struct S
{
char name[20];
int age;
float score;
};
int main()
{
char buf[200] = { 0 };
struct S s = { "张三", 20, 65.5f };
sprintf(buf, "%s %d %f", s.name, s.age, s.score);
printf("1以字符串的形式: %s\n", buf);//1
struct S t = {0};
sscanf(buf, "%s %d %f", t.name, &(t.age), &(t.score));
printf("2按照格式打印 : %s %d %f\n", t.name, t.age, t.score);//2
return 0;
}
功能说明:
用sscanf从test.txt中读取数据到字符串buf中
用sprintf把buf打印在屏幕上(当然也可以用printf打印字符串的操作)
3.文件顺序读写回顾
这里以fwrite() fread()二进制读写为例
ptr为要操作的数据的地址,size为单个数据大小
count为数据数量,stream为对应文件地址
实例一:
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5 };
FILE*pf = fopen("test.txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写数据
int sz = sizeof(arr) / sizeof(arr[0]);
fwrite(arr, sizeof(arr[0]), sz, pf);//以二进制的形式写进去的
fclose(pf);
pf = NULL;
return 0;
}
功能:以二进制形式把arr数组写到text.txt里面去
实例二:
#include <stdio.h>
int main()
{
int arr[5] = { 0 };
FILE* pf = fopen("test.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读数据
fread(arr, sizeof(arr[0]), 5, pf);//以二进制的形式读出来
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", arr[i]);//1 2 3 4 5
}
fclose(pf);
pf = NULL;
return 0;
}
功能:把二进制存储的文件读出来
结果:
PS:因为是顺序读写,所以先读3个再读2个会连续读取,也可以完成功能
#include <stdio.h>
int main()
{
int arr[5] = { 0 };
FILE* pf = fopen("test.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读数据
fread(arr, sizeof(arr[0]), 3, pf);//以二进制的形式读出来
int i = 0;
for (i = 0; i < 3; i++)
{
printf("%d ", arr[i]);//1 2 3 4 5
}
printf("\n");
fread(arr, sizeof(arr[0]), 2, pf);//以二进制的形式读出来
int j = 0;
for (j = 0; j < 2; j++)
{
printf("%d ", arr[j]);//1 2 3 4 5
}
fclose(pf);
pf = NULL;
return 0;
}
结果:
实例三:
int main()
{
int arr[5] = { 0 };
FILE* pf = fopen("test.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读数据-以二进制的形式写进去的
int i = 0;
while (fread(&arr[i], sizeof(int), 1, pf))
{
printf("%d ", arr[i]);
i++;
}
fclose(pf);
pf = NULL;
return 0;
}
功能:
实例二的优化写法,因为只要能读出fread就会返回true从而一个一个读,不用提前预设要读多少个。
4.文件的随机读写
4.1.fseek
根据文件指针的位置和偏移量来定位文件指针。
第一个参数为文件指针;第二个参数是偏移量;
第三个参数有三个可选,为参考位置:起始位置/当前位置/结尾位置
实例:
对于test.txt文件做以下操作
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
int ch = fgetc(pf);
printf("%c\n", ch);
fseek(pf, 4, SEEK_CUR);
ch = fgetc(pf);
printf("%c\n", ch);
fseek(pf, 5, SEEK_SET);
ch = fgetc(pf);
printf("%c\n", ch);
fseek(pf, -4, SEEK_END);
ch = fgetc(pf);
printf("%c\n", ch);
fclose(pf);
pf = NULL;
return 0;
}
过程:
4.2.ftell
返回文件指针相对于起始位置的偏移量
实例:
对于这个文件
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
int ch = fgetc(pf);
printf("%c\n", ch);//a
fseek(pf, -4, SEEK_END);
/*fseek(pf, 0, SEEK_END);*/
printf("%d\n", ftell(pf));
fclose(pf);
pf = NULL;
return 0;
}
过程
4.3. rewind
让文件指针的位置回到文件的起始位置
实例:
对于文件
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
int ch = fgetc(pf);
printf("%c\n", ch);//a
fseek(pf, -4, SEEK_END);
ch = fgetc(pf);
printf("%c\n", ch);//d
rewind(pf);
ch = fgetc(pf);
printf("%c\n", ch);//a
fclose(pf);
pf = NULL;
return 0;
}
5.文件读取结束的判定
5.1.被错误使用的 feof
牢记:在文件读取过程中,不能用feof函数的返回值直接来判断文件的是否结束。
feof 的作用是:当文件读取结束的时候,判断是读取结束的原因是否是:遇到文件尾结束。即已经结束后判断结束的原因。
PS:feof与ferror
-
文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
例如:
• fgetc 判断是否为 EOF .
• fgets 判断返回值是否为 NULL .
-
二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
实例:
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读取
int ch = 0;
while ((ch = fgetc(pf)) != EOF)
{
printf("%c\n", ch);
}
//判断是什么原因导致读取结束的
if (feof(pf))//非0值,遇到文件末尾,正常结束
{
printf("遇到文件末尾,读取正常结束\n");
}
else if (ferror(pf))
{
perror("fgetc");
}
return 0;
}
6.文件缓冲区
ANSIC 标准采用“缓冲文件系统” 处理的数据⽂件的,所谓缓冲⽂件系统是指系统⾃动地在内存中为程序中每⼀个正在使⽤的⽂件开辟⼀块“⽂件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。
打个比喻,同学要问老师问题。如果总是一个一个问,那么老师没法干活或只能给 一个同学答疑。先把问题整理好,憋个5个10个再统一问。这就是文件缓冲区。
7.结语
基本理解+能够使用=学会了
希望有些帮助~~