//序列化:将链表里的数据写入文件
//反序列化:将文件中的数据构建链表
//stdin:标准输入——》键盘 有缓冲区
//stdout:标准输出流——》屏幕
//stderr:标准出错流——》把错误信息输入到屏幕 无缓冲区
文件目录结构也叫B数结构
B/B+树是为了磁盘或其它存储设备而设计的一种平衡多路查找树(相对于二叉,B树每个内节点有多个分支),与红黑树相比,在相同的的节点的情况下,一颗B/B+树的高度远远小于红黑树的高度(在下面B/B+树的性能分析中会提到).B/B+树上操作的时间通常由存取磁盘的时间和CPU计算时间这两部分构成,而CPU的速度非常快,所以B树的操作效率取决于访问磁盘的次数,关键字总数相同的情况下B树的高度越小,磁盘I/O所花的时间越少.
文件操作格式:
文件操作函数
一.格式化函数
printf()将ptr指向的字符串格式化后打印到屏幕,底层调用了sprintf_s()
int printf(const char* ptr, ...);
sprintf_s()格式化字符串函数
将ptr指向的字符串格式化之后(格式成一个个字符)存入到buff中
int sprintf(char* buff, const char* ptr, ...);
VS2019中不支持sprintf(),因为不安全,数据可能将buff充爆,所以替换它的函数是sprintf_s()
int sprintf_s(char* buff, size_t buffcount,const char* ptr, ...);
fprintf()把任意类型的数据转换成字符串,并将格式化后的数据输入到指定的文件上面
//带缓冲区,先将文件写入缓冲区,等待关闭文件时(close(fp)才会将缓冲区数据写入fp指向的文件
int fprintf(FILE* fp, const char* ptr, ...);
二.打开文件函数
FILE* fopen(const char* filname, const char* mode);
VS2019将fopen进行了扩充fopen_s()
三.fopen()与fopen_s()的区别:
- 返回值:
fopen() :返回值是一个文件指针,若打开文件成功,返回一个指向该文件的文件指针,打开失败返回nullptr
fopen_s():返回值是一个int类型 - 参数:
fopen_s(): 第一个参数是一个存放一级文件指针的指针,所以传入的参数是一级文件指针的地址。若文件打开失败,一级文件指针依旧为nullptr
FILE* fp = nullptr;
//fp = fopen("C:/Users/86152/xsy/xsy.txt", "w");
errno_t res = fopen_s(&fp, "C:/Users/86152/xsy/xsy.txt", "w");
if (fp == nullptr)
{
printf("fopen file err %d\n", res);
return 1;
}
errno_t res = fopen_s(&fp, "C:/Users/86152/xsy/xsy.txt", "w");
fopen()建立的对象并不在用户区(栈区,数据区…),而是在内核区,由fp指向该空间,操作系统就会让该空间和磁盘或者硬盘中的某一个文件关联起来,若fp不为nullptr,则建立关系成功,fprintf()会将数据写入该内核对象指向的一块缓冲区,当程序执行fclose(fp),才会将缓冲区数据回写入磁盘中的文件。 然后将内核对象的引用计数降1为0,内核就会将该对象关闭,此时fp指向的内核对象没有了,fp就变成了失效指针
1.文本文件:
将数值转换成ASCLL码字符存放到文件中
2.二进制文件
内存中是什么格式原封不动在文件中就是什么格式
//二进制写文件函数
fwrite(ar, sizeof(int), n, fp);//ar是缓冲区;每个元素的大小;需要写n个元素;写到fp指向的文件中
四.读取文件
数据都会先存放在缓冲区
scanf( “%d”,&a);// 从标准输入设备stdin读取数据给 a
sscanf( buff, “%d” ,&a );//从缓冲区buff中按%d 的格式读取数据给 a
fscanf( fp, “%d” ,&a);//从fp所指向的文件中读取数据给a
五.处理文本文件的函数
1.得到单个字符:getchar()与fgetc()
char ch = '\0';
scanf_s("%c",&ch);
ch = getchar();//stdin
//getchar()返回一个整型值,该函数从键盘获取到字符时将该字符的ASCLL码值给ch
//等价于
ch = fgetc(stdin);
2.得到字符串:gets_s()与fgets()
//得到字符串,有效字符串个数n-1
char buffa[128];
char buffb[128];
scanf_s("%s", buffa,128);//把空格当作输入结束符
gets_s(buffa, 128);//返回值是一个指针,指向buffa的指针。将回车作为输入结束
//等价于
fgets(buffb, 128, stdin);//将回车作为输入结束
3.将单个字符送到屏幕:putchar()
//将字符打印到屏幕
const int n = 20;
char ch = 'a';
char stra[n] = { "yhping hello" };
putchar(ch);putchar('\n');
//等价于 printf("%c \n",ch);
4.将字符串送到屏幕:puts()
//将字符打印到屏幕
const int n = 20;
char ch = 'a';
char stra[n] = { "yhping hello" };
puts(stra);//将stra字符串送到屏幕,类似与printf(),但是printf()可以加'\n'
puts("\n");
5.文件一次读一个字符:
char ch;
FILE* fp = nullptr;
errno_t res = fopen_s(&fp, "7.12文件操作.cpp", "r");
if (fp == nullptr)
{
printf("fopen file err\n");
return res;
}
// feof()判断是否到达文件末尾,若到达文件末尾返回为真,否则返回为假
while (!feof(fp))//未到底文件末尾返回假,求反为真
{
ch = fgetc(fp);
printf("%c", ch);
Sleep(200);//每打印一个字符睡眠200ms
}
fclose(fp);
fp = nullptr;
return 0;
6.文件一次读一行:
char buff[10];//最多一次读9个字符
FILE* fp = nullptr;
errno_t res = fopen_s(&fp, "7.12文件操作.cpp", "r");
if (fp == nullptr)
{
printf("fopen file err\n");
return res;
}
// feof()判断是否到达文件末尾,若到达文件末尾返回为真,否则返回为假
while (!feof(fp))//未到底文件末尾返回假,求反为真
{
fgets(buff, 10, fp);
printf("%s", buff);
Sleep(20);//每打印一个字符睡眠200ms
}
fclose(fp);
fp = nullptr;
return 0;
7.将一个文件打开,从该文件每读入一行,写入到新文件中:fgets()与fputs()
char buff[10];//最多一次读9个字符
FILE* fp = nullptr;
errno_t res = fopen_s(&fp, "7.12文件操作.cpp", "r");
if (fp == nullptr)
{
printf("fopen file err\n");
return res;
}
FILE* fw = nullptr;
errno_t resw = fopen_s(&fw, "C:/Users/86152/xsy/xsy.txt", "w");
if (fw == nullptr)
{
printf("fopen file err\n");
return resw;
}
// feof()判断是否到达文件末尾,若到达文件末尾返回为真,否则返回为假
while (!feof(fp))//未到底文件末尾返回假,求反为真
{
fgets(buff, 10, fp);
fputs(buff,fw);
Sleep(20);//每打印一个字符睡眠200ms
}
fclose(fp);
fp = nullptr;
return 0;
8.实现文件拷贝函数
int main(int argc, char* argv[])
{
if (argc < 3)
{
printf("copy file error \n");
//return 1;
exit(EXIT_FAILURE);
}
//argv[1] srcfilename;原文件名
//argv[2] destfilename;目标文件名
FILE* fr = nullptr;//读文件
FILE* fw = nullptr;//写文件
//fr = fopen(argv[1], "r");
errno_t rx = fopen_s(&fr, argv[1], "r");
//fw = fopen(argv[2], "w");
errno_t wx = fopen_s(&fw, argv[2], "w");
if (fr == nullptr || fw == nullptr)
{
printf("open file failure \n");
exit(EXIT_FAILURE);
}
char ch = '\0';
while (!feof(fr))
{
ch = fgetc(fr);//从fr中读取一个字符给ch
fputc(ch, fw);//把字符写入到fw中
putchar(ch);//stdout
}
fclose(fr);
fr = nullptr;
fclose(fw);
fw = nullptr;
return 0;
}
六.主函数参数问题
1.不带参数
目的:程序运行过程中不希望从外界接收参数
int main()
{
}
2.带2个参数
程序运行过程中需要从外界获取参数
参数代表的意思:
- argc:参数个数,包括当前运行的程序名称
- argv[]:二级指针,存放外界传入参数的地址
int main(int argc,char*argv[])
{
}
3.带3个参数
参数代表的意思:
- argc:参数个数,包括当前运行的程序名称
- argv[]:二级指针,存放外界传入参数的地址
- arge[]:存放环境变量
int main(int argc,char*argv[],char *arge[])
{
}
七.关于主函数形参的问题
1.argc,argv[]都是主函数形参,他们存在的空间都在主函数的栈帧里面,但是用户从键盘输入的形参(字符串)在哪里呢?
- 代码区:不行,因为代码区编译链接后就已经固定了
- 堆区:不行,因为需要malloc
- 数据区:?
- 栈区:?
判断方法:
可以打印形参地址。定义全局变量,打印全局变量地址空间;定义局部变量,打印局部变量地址空间;观察形参地址和哪个变量地址近,若和局部变量地址离的近,形参就在栈区;和全局变量地址离的近就在数据区。
2.输入的参数无论是字符,字符串,整型…主函数都按照字符串处理
用户输入:125.23.34.56==》主函数处理成:“125.23.34.56”
所以就需要将字符串解析成整型数据: atoi()
八.文件位置指针
打开文件时文件定位指针就在文件首
文件指针的特点:
- 1.文本文件中:
在文本文件中文件定位指针是无规律的,它是按照用户存放的数据值给出的位置信息,和数据在文本中的长度有关系 - 2.二进制文件中:
二进制文件在内存中是什么样子在文件中就是什么样子
1.文本文件形式实现
int main()
{
const int n = 10;
int ar[n] = { 12,23,34,45,1,234,2,345,5,8899};//数据长度不等
FILE* fw = nullptr;
errno_t err = fopen_s(&fw, "C:/Users/86152/xsy/ar.txt","w");
if (fw == nullptr)
{
printf("open file error \n");
exit(1);
}
for (int i = 0;i < n;++i)//向文件中写入数据
{// fw是文件流
fprintf(fw, "%d ", ar[i]);//将ar[i]中的数据写入fw指定的文件中,注意:此处只是写入到缓冲区,关闭文件才写入到文件
// “%d " 是控制格式,每写入一个数据用空格隔开
}
fclose(fw);
fw = nullptr;
return 0;
}
ftell()实现文件位置定位
fell()返回值是long型—>4字节整型值,表示的范围是2G,即以该函数控制文件,文件大小最大为2G
fgetpos()使用ps作为文件定位指针,是unsigned long long ------->64字节,表示的文件范围就是2^64大小
int main()
{
int val;
const int n = 10;
FILE* fr = nullptr;
errno_t err = fopen_s(&fr, "C:/Users/86152/xsy/ar.txt", "r");
if (fr == nullptr)
{
printf("open file error \n");
exit(1);
}
int pos = ftell(fr);//获得文件位置信息
while (!feof(fr))//判断文件是否到尾部
{
fscanf_s(fr, "%d", &val);//按照%d的格式将fr文件中的数据读到val中
printf("val=%d ", val);
pos = ftell(fr);
printf("pos = %d \n", pos);
}
fclose(fr);
fr = nullptr;
return 0;
}
文本文件在文件中存放是按照数据对应的ASCLL码值存放的,由于数据是按照空格分隔存放,所以数据与数据之间有空格的ASCLL码值(20)
在文本文件中文件定位指针是无规律的,它是按照用户存放的数据值给出的位置信息,和数据在文本中的长度有关系
2.二进制文件形式实现
二进制文件在内存中是什么样子在文件中就是什么样子
//初始化二进制文件
int main()
{
const int n = 10;
int ar[n] = { 12,23,34,45,1,234,2,345,5,8899 };//数据长度不等
FILE* fw = nullptr;
errno_t err = fopen_s(&fw, "C:/Users/86152/xsy/bfile.txt", "wb");
if (fw == nullptr)
{
printf("open file error \n");
exit(1);
}
fwrite(ar, sizeof(int), n, fw);//将缓冲区ar中的数据,数据类型是整型,n个数据写入fw
fclose(fw);
fw = nullptr;
return 0;
}
//二进制文件定位指针
int main()
{
int val;
const int n = 10;
FILE* fr = nullptr;
errno_t err = fopen_s(&fr, "C:/Users/86152/xsy/bfile.txt", "rb");
if (fr == nullptr)
{
printf("open file error \n");
exit(1);
}
int pos = ftell(fr);//获得文件位置信息
/*fpos_t ps;
fgetpos(fr, &ps);*/
for(int i=0;i<n;++i)
{
fread(&val, sizeof(int), 1, fr);//从fr中每次读一个整型给val
pos = ftell(fr);
printf("val = %4d pos= %4d \n",val, pos);
}
fclose(fr);
fr = nullptr;
return 0;
}
二进制文件在内存中是什么样子在文件中就是什么样子
3. fseek()可以计算文件大小
计算文件大小并将文件内容写入buff 步骤:
- 1.把文件以二进制方式打开
- 2.设置文件指针在文件末尾
- 3.读文件指针的位置(该位置就是文件的大小)
- 4.堆上申请空间
- 5.文件定位指针移动到文件首(fread()读数据是从文件首开始读的)
int main()
{
FILE* fr = nullptr;
errno_t err = fopen_s(&fr, "7.19File.cpp", "rb");
if (fr == nullptr)
{
printf("open file error \n");
exit(1);
}
fseek(fr, 0, SEEK_END);//文件定位指针直接偏移到末尾
int len = ftell(fr) + 1;//文件定位指针的位置就是文件字节个数
char* buff = (char*)malloc(sizeof(char) * len);
if (buff == nullptr)
{
return 1;
}
rewind(fr);//fseek(fr, 0, SEEK_SET);
fread(buff, sizeof(char), len, fr);
buff[len - 1] = '\0';
printf("%s", buff);
fclose(fr);
fr = nullptr;
free(buff);
buff = nullptr;
return 0;
}