版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_39286218/article/details/78759353
下面讨论二进制版较结构体版的不同,优势,两难,改进之处:
不同:
(一)全局变量
结构体版:
struct record users[upNum]; //银行所有用户
int N; //实际的用户数目
二进制版:
int N;
FILE *fp;
全局变量的结构体数组->子函数中定义指向结构体的指针
fp由局部变量->全局变量
(二)main函数
结构体版:
int main()
{
printf("+----------------------+\n");
printf("+ 欢迎光临CSDN银行 +\n");
printf("+----------------------+\n");
if (pass())
{
readData();
work();
writeData();
}
return 0;
}
二进制版:
int main()
{
FILE *fp;
printf("+----------------------+\n");
printf("+ 欢迎光临我家银行 +\n");
printf("+----------------------+\n");
if (pass())
{
exchange(); //将数据从文本文件读到二进制文件
if((fp=fopen("account.bin", "rb+"))==NULL) //文件不存在时重建,文件已经存在时,将保留原数据
{
printf("数据文件打开失败,退出程序....");
exit(1);
}
fseek(fp, 0, SEEK_END);
N = ftell(fp)/sizeof(record); //ftell用于得到文件位置指针当前位置相对于文件首的偏移字节数,除以sizeof(struct record),得到的是当前用户的数目
work();
fclose(fp);
}
return 0;
}
结构体版readData(),writeData() 需要用到大数组操作,需要读写所有的数据信息;
二进制版运用随机读写,只要定位到某账户所在的文件指针位置,不再需要内存中的大数组。
(三)二进制的业务员文件及存储
结构体版:
int pass()
{
...
if ((fp=fopen("worker.txt", "r"))==NULL)
{
printf("password file cannot open!");
exit(0);
}
fscanf(fp, "%s %s", sNameInFile, sPassInFile); //从文件中读业务员用户名和密码密码
fclose(fp);
//进入系统,密码三次不对将退出
do
{
...
}
while(iTry);
return right;
}
二进制版:
int pass()
{
...
if ((fp=fopen("password.dat", "rb"))==NULL)
{
printf("第一次使用,将设置初始操作员账户:\n");
printf("请输入用户名:");
scanf("%s",sName);
printf("请输入密码:");
iPass1=inputPassword(); //输入密码1
printf("确认密码:");
iPass2=inputPassword(); //输入密码2
if(iPass1==iPass2)
{
fp=fopen("password.dat", "wb");
fwrite(sName, sizeof(sName), 1, fp);
fwrite((char*)&iPass1, sizeof(int), 1, fp);
right = 1;
fclose(fp);
}
else
{
printf("两次密码不一致,设置不成功!\n");
exit(0);
}
}
else
{
fread(sNameInFile, sizeof(sNameInFile), 1, fp); //从文件中读业务员用户名
fread((char*)&iPassInFile, sizeof(int), 1, fp); //密码
fclose(fp);
//进入系统,密码三次不对将退出
do
{
...
}
while(iTry);
}
return right;
}
结构体版中用字符数组保存业务员密码;二进制版中密码采用整型,添加了打开失败的处理
(四)从文件中读取以及更新内容——以取款为例
结构体版:
void withdraw()
{
...
printf("账号:");
scanf("%d", &id);
who = search(id); //根据账号查询用户,返回用户的下标
if(who<0) //说明id账户不存在
{
printf("该用户不存在,取款失败!\n");
}
else
{
if(users[who].status==0)
{
printf("户主姓名:%s\n", users[who].name);
printf("密码:");
iPass=inputPassword();
if(iPass!=users[who].password)
{
printf("输入密码错误,取款失败!\n");
}
else
{
printf("输入取款额:");
scanf("%lf", &money);
if(money>users[who].balance) //亲,不玩透支
{
printf("余额不足,取款失败!\n");
}
else
{
users[who].balance-=money;
printf("取款后,还有%.2f元. \n",users[who].balance);
}
}
}
else if(users[who].status==1)
...
else
...
}
return;
}
int search(int id)
{
int index=-1;
int i;
for(i=0; i<N; i++)
{
if(users[i].account==id)
{
index=i;
break; //找到了,立即退出循环
}
}
return index; //若找到,其值在0~N-1间,否则,保持-1
}
二进制版:
void withdraw()
{
...
record *user;
user = getuser(); //输入并查询用户,不存在返回NULL
if(user!=NULL)
{
if(user->status==0)
{
printf("户主姓名:%s\n", user->name);
printf("密码:");
iPass=inputPassword();
if(iPass!=user->password)
{
printf("输入密码错误,取款失败!\n");
}
else
{
printf("输入取款额:");
scanf("%lf", &money);
if(money>user->balance) //亲,不玩透支
{
printf("余额不足,取款失败!\n");
}
else
{
user->balance-=money;
writeData(user);
printf("取款后,还有%.2f元. \n",user->balance);
}
}
}
else if(user->status==1)
...
else
...
free(user);
}
return;
}
record* getuser()
{
FILE *fp;
if((fp=fopen("account.bin", "rb+"))==NULL) //文件不存在时重建,文件已经存在时,将保留原数据
{
printf("数据文件打开失败,退出程序....");
exit(1);
}
int id;
printf("账号:");
scanf("%d", &id);
record *user = NULL;
if(id>=N+10001)//说明账户不存在
{
printf("该用户不存在,查询完毕!\n");
}
else
{
fseek(fp,(id-10001)*sizeof(record), SEEK_SET);
user=(record*)malloc(sizeof(record));
fread((char*)user, sizeof(record), 1, fp);
}
fclose(fp);
return user; //若找不到,user为NULL
}
void writeData(record *user)
{
FILE *fp;
if((fp=fopen("account.bin", "rb+"))==NULL) //文件不存在时重建,文件已经存在时,将保留原数据
{
printf("数据文件打开失败,退出程序....");
exit(1);
}
//先在文件中定位
long i = user->account -10001;
fseek(fp, (long)i*sizeof(record), SEEK_SET);
fwrite((char*)user, sizeof(record),1,fp);
fclose(fp);
}
二进制版随机读取并采用动态分配内存的方式,避免大数组操作和节省空间(五)创建二进制文件account.bin用来保存储户信息
#define upNum 2000 //估计一个超出原实际储户的大数
void exchange(){
FILE *fp;
record users[upNum]; //存放数据的一段内存空间
int i=0,N;
/*事先将储户信息输入到文本文件中*/
if((fp=fopen("account.dat","r"))==NULL){
printf("account.dat cannot be opened");
exit(1);
}
/*将文本文件中的信息读到内存*/
while(fscanf(fp,"%d %s %d %lf %d",&users[i].account, users[i].name, &users[i].password, &users[i].balance,&users[i].status ) != EOF)
i++;
N=i;
fclose(fp);
if((fp=fopen("account.bin","wb"))==NULL){
printf("account.bin cannot be opened");
exit(1);
}
/*将内存数据读到二进制文件中*/
fwrite((char*)&users[0],sizeof(users[0]),N,fp);
fclose(fp);
}
优势:
二进制文件:空间省、效率高、保密。结合随机读取,自由灵活,适用大文件
两难:
fp作为全局变量,又是指针,在整个文件中起作用,生存周期长
若将fp作为局部变量(定义FILE *fp;fopen();fclose();),但会在writeData(),getuser()中频繁调用,使用外存,过程缓慢
改进之处:索引+随机访问
实际应用中数据文件未必顺序存储,但账号不会重复,
使用数据在内存中的偏移量代替序号,声明第几条记录
把偏移量和账号抽取出来作为排序前索引,根据账号查出是第几条记录
通过以账号进行排序后的索引文件,将索引数据读到内存,快速查找到账户信息
查找账号->偏移量->储户数据信息