#include "stdio.h"
#include "string.h"
#include "stdlib.h"
typedef struct student
{
int qinshi;
int chuangwei;
char name[20];
char phone[12];
long num;
}STU;
void shuru()
{ int i,n;
STU a;
FILE *fp;
fp=fopen("C:\\学生宿舍管理系统.dat","wb");
if(fp==NULL)
{
printf("!");
return;
}
system("cls");
printf("录入的个数是: ");
scanf("%d",&n);
for(i=0;i<n;i++)
{
printf("\t寝室号:");
scanf("%d",&a.qinshi);
printf("\t床位:");
scanf("%d",&a.chuangwei);
printf("\t姓名:");
scanf("%s",a.name);
printf("\t手机号:");
scanf("%s",a.phone);
printf("\t学号:");
scanf("%ld",&a.num);
fwrite(&a,sizeof(STU),1,fp);
}
fclose(fp);
}
void zhuijia()
{
int i,n;
STU a;
FILE *fp;
system("cls");
if((fp=fopen("C:\\学生宿舍管理系统.dat","ab"))==NULL)
{printf("error!\n");exit(0);}
printf("\n\n\t请输入追加人数:");
scanf("%d",&n);
for(i=0;i<n;i++)
{
printf("\t寝室号:");
scanf("%d",&a.qinshi);
printf("\t床位:");
scanf("%d",&a.chuangwei);
printf("\t姓名:");
scanf("%s",a.name);
printf("\t手机号:");
scanf("%s",a.phone);
printf("\t学号:");
scanf("%ld",&a.num);
fwrite(&a,sizeof(STU),1,fp);
}
fclose(fp);
}
void xianshi()
{STU a;
FILE*fp;
system("cls");
if((fp=fopen("C:\\学生宿舍管理系统.dat","rb"))==NULL)
{
printf("error!\n");exit(0);
}
rewind(fp);
printf("\n\n\n\t\b\b寝室号\t床位\t姓名\t手机号\t学号\n");
while(fread(&a,sizeof(STU),1,fp)!=0)
printf("\t%d\t%d\t%s\t%s\t%ld\n",a.qinshi,a.chuangwei,a.name,a.phone,a.num);
fclose(fp);
}
void chaxunbyname()
{
int f=0;
char na[20];
STU a;
FILE *fp;
fp=fopen("C:\\学生宿舍管理系统.dat","rb");
if(fp==NULL)
{printf("无法打开!\n");exit(1);}
printf("请输入要查找姓名:");
scanf("%s",na);
while(fread(&a,sizeof(STU),1,fp)!=0)
if(strcmp(na,a.name)==0)
{
printf("寝室号\t床位\t姓名\t手机号\t学号\n");
printf("%d\t%d\t%s\t%s\t%ld\n",a.qinshi,a.chuangwei,a.name,a.phone,a.num);
f=1;
}
if(f==0) printf("没有该学生信息。");
fclose(fp);
}
void chaxunbyxuehao()
{
int f=0,x=0;
STU a;
FILE *fp;
fp=fopen("C:\\学生宿舍管理系统.dat","rb");
if(fp==NULL)
{printf("打开失败!\n");exit(1);}
scanf("%d",&x);
while(fread(&a,sizeof(STU),1,fp)!=0)
if(x==a.num)
{
printf("寝室号\t床位\t姓名\t手机号\t学号\n");
printf("%d\t%d\t%s\t%s\t%ld\n",a.qinshi,a.chuangwei,a.name,a.phone,a.num);
f=1;
}
if(f==0) printf("没有该寝室的信息。");
fclose(fp);
}
void chaxun()
{
int a;
system("cls");
printf("\n\n\t\t你现在已经进入学生宿舍管理系统\n");
printf("\t\t\t按姓名查询输入1\n");
printf("\t\t\t按学号查询输入2\n");
printf("\t\t\t退出输入3\n");
printf("\t\t\t请输入命令: ");
scanf("%d",&a);
switch(a)
{
case 1:chaxunbyname();break;
case 2:chaxunbyxuehao();break;
case 3:break;
}
}
void shanchu()
{
int s;
struct student a[10];
int i=0, n, f=0, k;
long num;
FILE *fp;
printf("\t\t\t 请输入管理密码!");
scanf("%d", &s);
if(s!=0)printf("管理密码错误!");
else
{
fp=fopen("C:\\学生宿舍管理系统.dat","r");
if (fp==NULL)
{
printf("打开C:\\学生宿舍管理系统.dat失败!\n");
exit(1);
}
fseek (fp,0,2) ;
n=ftell(fp)/sizeof(struct student) ;
rewind (fp);
printf("输入学号:");
scanf ("%ld", &num);
for(i=0;i<n;i++)
fread (&a[i],sizeof(STU),1,fp);
for(i=0;i<n;i++)
if (num==a[i].num)
{
printf("按任意键删除");
f=1;
k=1;
break;
}
else
f=0;
fclose(fp);
if(f==0)
printf("未找到对应学生信息\n");
else
{
for(i=k;i<n-1;i++)
a[i]=a[i+1];
n--;
fp=fopen("C:\\学生宿舍管理系统.dat","w");
if (fp==NULL)
{
printf("打开C:\\学生宿舍管理系统.dat 失败!\n");
exit(1);
}
for (i=0;i<n;i++)
fwrite (&a[i], sizeof(struct student),1,fp);
fclose(fp);
}
getchar();getchar();
}
}
void main()
{
int a;
do
{
printf("\n\n\n\t\t\t欢迎进入学生宿舍管理系统\n");
printf("\t\t\t录入学生信息输入1\n");
printf("\t\t\t显示学生信息输入2\n");
printf("\t\t\t查询学生信息输入3\n");
printf("\t\t\t追加学生信息输入4\n");
printf("\t\t\t删除学生信息输入5\n");
printf("\t\t\t退出系统输入7\n");
printf("\t\t\t输入命令: ");
scanf("%d",&a);
switch(a)
{
case 1:shuru();break;
case 2:xianshi();break;
case 3:chaxun();break;
case 4:zhuijia();break;
case 5:shanchu();break;
case 7:exit(0);
}
}while(a!=0);
}
要添加一个修改宿舍信息的功能,可以按照以下步骤进行修改:
struct student
结构体中添加需要修改的字段,例如 qinshi
、chuangwei
、name
、phone
和 num
。xiugai
。xiugai
函数。xiugai
函数中实现以下逻辑:"C:\\学生宿舍管理系统.dat"
以读写模式。下面是修改后的代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct student
{
int qinshi;
int chuangwei;
char name[20];
char phone[12];
long num;
} STU;
void shuru()
{
// 之前的代码不变
}
void zhuijia()
{
// 之前的代码不变
}
void xianshi()
{
// 之前的代码不变
}
void chaxunbyname()
{
// 之前的代码不变
}
void chaxunbyxuehao()
{
// 之前的代码不变
}
void chaxun()
{
// 之前的代码不变
}
void shanchu()
{
// 之前的代码不变
}
void xiugai()
{
int f = 0;
long num;
STU a;
FILE* fp;
fp = fopen("C:\\学生宿舍管理系统.dat", "rb+");
if (fp == NULL)
{
printf("无法打开文件!\n");
exit(1);
}
printf("请输入要修改的学号: ");
scanf("%ld", &num);
while (fread(&a, sizeof(STU), 1, fp) != 0)
{
if (num == a.num)
{
f = 1;
printf("请输入新的寝室号: ");
scanf("%d", &a.qinshi);
printf("请输入新的床位: ");
scanf("%d", &a.chuangwei);
printf("请输入新的姓名: ");
scanf("%s", a.name);
printf("请输入新的手机号: ");
scanf("%s", a.phone);
fseek(fp, -sizeof(STU), SEEK_CUR);
fwrite(&a, sizeof(STU), 1, fp);
break;
}
}
if (f == 0)
printf("未找到对应学生信息\n");
fclose(fp);
}
int main()
{
int a;
do
{
printf("\n\n\n\t\t\t欢迎进入学生宿舍管理系统\n");
printf
("\t\t\t录入学生信息输入1\n");
printf("\t\t\t显示学生信息输入2\n");
printf("\t\t\t查询学生信息输入3\n");
printf("\t\t\t追加学生信息输入4\n");
printf("\t\t\t删除学生信息输入5\n");
printf("\t\t\t修改学生信息输入6\n");
printf("\t\t\t退出系统输入7\n");
printf("\t\t\t输入命令: ");
scanf("%d", &a);
switch (a)
{
case 1:
shuru();
break;
case 2:
xianshi();
break;
case 3:
chaxun();
break;
case 4:
zhuijia();
break;
case 5:
shanchu();
break;
case 6:
xiugai();
break;
case 7:
exit(0);
}
} while (a != 0);
return 0;
}
在修改后的代码中,我添加了一个名为 xiugai
的函数来实现修改宿舍信息的功能。在主函数中添加了一个选项 6 来调用该函数。当用户选择 6 时,会调用 xiugai
函数进行宿舍信息的修改。用户需要输入要修改的学号,并按照提示输入新的寝室号、床位、姓名和手机号。程序会根据用户的输入修改对应的字段,并将修改后的信息写入文件中。
strncpy函数与strcpy函数不能说毫无差别,只能说功能一模一样。唯一不同的是加入了:限制数。
这也使得strcpy函数的弊端得到了根本优化!
char *strcpy( char *strDestination, const char *strSource ,size_t len);
size_t len的出现,就意味着这个函数将不再“失控”。虽然功能与strcpy不尽相同,但实现的步骤却有了不同:size_t len提供给拷贝任务运行限制,如果符合限制条件,那么拷贝任务继续。如果触及限制条件,那么拷贝停止。
注意!它的结果将不再以源字符串中的NULL字节结尾。
同样的,在开始进行函数使用前,我们思考这三个情境:
1、如果限制数len的数比源字符串的元素位数大,结果是如何?
2、如果源字符串的元素位数比限制数len的数大,结果是如何?
3、如果限制数len的数比目标字符串的元素位数大,结果是如何?
情境一:
如果限制数len的数比源字符串的元素位数大,结果是如何?
//1、如果限制数len的数比源字符串的元素位数大,结果是如何?
int main()
{
char vate1_char[15] = "wwwwwwwwwwwwww";
char vate2_char[] = "ssssss";
int len = 10;
char* ret = strncpy(vate1_char, vate2_char, len);//10为限制数
printf("%s", ret);
return 0;
}
结果:
情境二:
如果源字符串的元素位数比限制数len的数大,结果是如何?
//2、如果源字符串的元素位数比限制数len的数大,结果是如何?
int main()
{
char vate1_char[15] = "wwwwwwwwwwwwww";
char vate2_char[] = "ssssss";
int len = 3;
char* ret = strncpy(vate1_char, vate2_char, len);
printf("%s", ret);
return 0;
}
结果:
情境三:
如果限制数len比目标字符串的元素位数大,结果是如何?
int main()
{
char vate1_char[15] = "wwwwwwwwwwwwww";
char vate2_char[] = "ssssss";
int len = 20;
char* ret = strncpy(vate1_char, vate2_char, len);
printf("%s", ret);
return 0;
}
结果:
情境一分析:
问题1:如果限制数len的数比源字符串的元素位数大,结果是如何?
char *strcpy( char *strDestination, const char *strSource ,size_t len);
如果限制数len的大小比strlen(strSource)->(源字符串)大,那么strncpy函数会将源字符串中的元素拷贝到目标字符串中,而len限制数未运行的拷贝数,也将会由等量的NULL来填充。
这是函数拷贝进行时vate1_char字符串内部的变化,而当调用结果以字符指针被传回时,在输出的过程中,只能读取到源字符串结尾的'\0',所以后面的字节都会被覆盖。
vate1_char数组内部元素摆放:
情境二分析:
问题2:如果源字符串的元素位数比限制数len的数大,结果是如何?
char *strcpy( char *strDestination, const char *strSource ,size_t len);
如果strlen(strSource)->(源字符串位数)比限制数len大时。那么,函数将依据len的大小,只拷贝源字符串中的len个数位到目标字符串中。而在结束时,不以NULL字节作为补齐,因为源字符串数位满足len限制数的调用需求,故结果将以目标字符串中的NULL为结束标志。
但发生源字符串大于len限制数时,那么只会拷贝源字符串中的len位,而原目标字符串中的其他数位也不会被覆盖,因为返回的结果是以原目标字符串中的NULL为结束标志。
vate1_char数组内部元素摆放:
情境三分析:
问题3:如果限制数len的数比目标字符串的元素位数大,结果是如何?
char *strcpy( char *strDestination, const char *strSource ,size_t len);
当限制数len大于strlen(trDestination)目标字符串时,不论源字符串的数位大小满足限制数len的条件与否,都会造成最后的内存溢出与非法修改操作。最后strncpy函数会完成它本职的拷贝工作,但是你根本不知道它将溢出的数位拷贝到了哪一块内存中!
现在再来回头看看其中的错误,其实大部分的错误都是可以被避免的,比如我们在使用拷贝函数前,先将目标字符串的大小进行合理的预留,那么bug就将减少%50!
猜想:那么为什么要用NULL来填充呢?
因为,strncpy函数限制数的加入,使得函数的使用变得更灵动化。但有时候,由于错误的限制数设定,导致strncpy函数的调用结果可能并不是一个字符串。原因在于:strncpy(有限制)函数不会像strcpy函数那样,会将源字符串中的NULL也拷入进目标字符串来作为停止标记。
所以,strncpy函数为了避免错误的发生,它会在限制数大于源字符串数的时候触发这个保护机制,即:不够的拷贝位由NULL来补齐,以确保结果是一个能用格式化输出printf->%s来打印出。
strncpy函数的特性:
具有限制性!
上代码:
char* my_strncpy(char* v_1, const char* v_2, int len)
{
assert(v_1 && v_2);//断言
char* ret = v_1;//将v_1首元素复制给*ret,用于返回值
while (len--)//交换一次限制数就--
{
*v_1++ = *v_2++;//拷贝
}
return ret;
}
int main()
{
char vate_1[25] = "我爱熬夜!也爱学习!";
char vate_2[] = "我爱生活!";
int len = 0;
puts("请输入拷贝数:");
scanf("%d", &len);
char* ret = my_strncpy(vate_1, vate_2, len);
printf("%s\n", ret);
return 0;
}
结果:
将源字符串中的5个位数拷贝至目标字符串中,可能有人会疑惑了:
为什么五个元素你却输入9个限制数,那岂不是拷贝了9次?
其实,在最初,char类型是不允许汉字的参入的,但由于计算机语言的迅速发展,亚太地区汉语、日语、韩语等没有办法在计算机中得到参入,严重影响了功能的实现,故添加了文字。但文字与字母不同,一个汉字等同于两个字母。所以在拷贝中,需要消耗两个拷贝数才能实现一个汉字的拷贝。
故:2+2+2+2+1=9.
字符串的追加操作,指的是在字符串的末尾插入另一个字符串,对目标字符串进行元素的增加。
strcat函数是一个无限制的字符串追加函数。
即:给定一个目标字符串与源字符串,那么它就会将源字符串元素全部插入目标字符串。
这个函数的顺利进行,必须要依托'\0',且是两个字符串参数都具有NULL字节结尾。可以这么理解:
目标字符串中的'\0',是插入元素(源字符串)首元素的地址(入口),而源字符串的'\0'则是出口,即结束标志。
图演:
以上是strcat函数的核心作用,但这这是为了让伙伴们先简要理解以下它是干什么的,好菜还在下面呢!
char *strcat( char *strDestination, const char *strSource );
strcat函数的两个参数必须是以'\0'为结尾的字符序,其返回的值是一个字符指针。
或许有人好奇:那如果两个参数都不为'\0'结尾的字符序结果会是如何?
那么接下来就让我们实验出真理吧!
老样子,开始前,我们需要怀揣着几个问题思考:
1、如果strcat函数参数部分双方不为字符串,结果是如何?
2、strcat函数可以自己(参数)调用自己(参数)吗?
好戏开始!
情境一:
如果strcat函数参数部分双方不为字符串('\0'结尾),结果是如何?
上代码:
int main()
{
char vate_1[20] = { 'h','a','p','p','y'};//字符数组
char vate_2[] = { 'l','i','f','e'};
char* ret = strcat(vate_1, vate_2);
printf("%s\n", ret);
return 0;
}
结果:
持续闪烁,最后程序挂掉。
如果给这连个非NULL结尾的字符序各自加上NULL,结果会不会有所改变 呢?
上代码:
int main()
{
char vate_1[20] = { 'h','a','p','p','y','\0'};//在字符数组尾部添加'\0'
char vate_2[] = { 'l','i','f','e','\0'};
char* ret = strcat(vate_1, vate_2);
printf("%s\n", ret);
return 0;
}
结果:
最后确实达到了我们的预期,但这是为什么呢?
情境二:
strcat函数可以自己(参数)调用自己(参数)吗?
上代码:
int main()
{
char vate_1[20] = "happy";
char vate_2[] = "life";
char* ret = strcat(vate_1, vate_1);
printf("%s\n", ret);
return 0;
}
结果:
接下来开始解析,小伙伴们,能看到这已经很棒啦!但请继续坚持,我保证最后你将会有所收获!
情境1解析:
问题:如果strcat函数参数部分双方不为字符串('\0'结尾),结果是如何?
如果stact函数的两个参数都不以NULL字节结束,那么,可怜的strcat函数根本就找不到进行的入口,更不要想出口在哪里,所以它一直在寻找目标字符串中的NULL字节,等到最后确实找不到了,那么这个函数也就挂了!
尽管幸运找到那两个入口与出口,那么所返回的值也必定是非法的!
而上述最后所作的修改:是在目标字符序与源字符序中各添加“NULL”字节,有了入口与出口,strcat函数就满血复活啦!
情境2解析:
strcat函数可以自己(参数)调用自己(参数)吗?
这里出现了一个有趣的例子,也是对strcpy函数的挑战。strcat函数能够对目标字符串进行直接的改动,而在情境二中:strcat函数首先找到vate_1的NULL字节,将其修改为源字符串的首元素:'h',注意!strcat函数能够对目标元素进行直接的修改,这就意味着作为参数的源字符串vate_1中的NULL字节也被修改为了:'h',
之前我们提到:目标字符串中的NULL字节,相当于strcat函数的一个切入点,即:“入口”,而源字符串中的NULL字节则相当于一个结束标志,即:“出口”。而上述情况则相当于函数有了入口,却没了出口!
那会发生什么情况呢?答案一定是内存的越界操作,可怜的strcat函数在苦苦的寻找那个出口,但久久未能找到,最后导致程序崩溃!
依据strcat函数的运行原理,它无法完成对同一字符序的自我追加操作,但strncat函数则对此进行了修善,待完成strcat函数的模拟实现后,我们就见分晓!
char *strcat( char *strDestination, const char *strSource );
我们举了两个strcat函数的使用情境,对此,从中我们可以总结出:
strcat函数在目标字符串结束符“NULL”上进行插入,将源字符串的首元素覆盖掉NULL字节,并逐个向后访问,直到找到源字符串尾部的NULL字节,并将其插入至目标字符串尾部后,函数才会停止。
接下来,我们就以此总结为实现目标,完成对strcat函数的模拟实现:
上代码:
//strcat模拟实现
char* my_strcat(char* value1_char, const char* value2_char)
{
assert(value1_char && value2_char);
char* temp = value1_char;//储存目标字符串的首地址
while (*value1_char)//寻找目标字符串'\0'
{
value1_char++;
}
while (*value1_char++ = *value2_char++)//在'\0'位置进行源字符串的拷贝
{
;
}
return temp;//返回
}
int main()
{
char value1_char[50] = "我热爱生活!也热爱学习!";
char value2_char[] = "愿世间充满欢乐!";
char* temp = my_strcat(value1_char, value2_char);
printf("%s", temp);
return 0;
}
//理解及思路
//1、保证目标字符串及源字符串的结尾都具备有‘\0’。
//2、目标字符串空间须是可修改的,且是足够大的。
//3、第一步需要找到目标字符串的‘\0’处。
//4、第二步,在目标字符串‘\0’处进行进行拷贝,将源字符串拷贝到目标字符串之后,直到'\0'结束。
//5、需要对目标字符串进行存地址,否则贸然返回目标字符串,所得到的将是尾部'\0\的地址,从而导致空白打印。
结果:
在这喧嚣内卷的世界中,我愿拼尽全力,去创那三分自留地,护一家长幼安详,与她安稳相守。也祝你们顺利!
要说strcat的缺陷,其实只要有两点:
1、不能作用与同一个参数的调用。
2、不具备灵活调控性。
对于问题1,如何在程序中创建一个可以修改宿舍信息的界面,你可以考虑使用图形用户界面(GUI)库来创建界面,比如Tkinter、PyQt等。以下是一个使用Tkinter库创建简单界面的代码示例:
import tkinter as tk
def update_dorm_info():
# 在这里实现宿舍信息的修改逻辑
pass
# 创建主界面窗口
window = tk.Tk()
# 创建宿舍信息的各个字段的输入框和标签
name_label = tk.Label(window, text="姓名:")
name_label.pack()
name_entry = tk.Entry(window)
name_entry.pack()
dorm_label = tk.Label(window, text="宿舍号:")
dorm_label.pack()
dorm_entry = tk.Entry(window)
dorm_entry.pack()
# 创建一个按钮用于触发宿舍信息的修改操作
update_button = tk.Button(window, text="修改", command=update_dorm_info)
update_button.pack()
# 进入消息循环
window.mainloop()
对于问题2,需要在界面中提供哪些可修改的宿舍信息字段,可以根据你的具体需求来决定。一般来说,可以提供一些常见的宿舍信息字段,比如姓名、学号、宿舍号等。
对于问题3,如何验证用户输入的宿舍信息是否合法,可以在宿舍信息修改的逻辑中添加输入验证的步骤。根据你的需求,可以对每个字段的输入进行一些验证,比如检查是否为空、是否符合特定的格式要求等。
对于问题4,如何在修改后及时更新宿舍信息并保存到数据库中,这涉及到数据库的操作。你可以使用数据库操作库来连接数据库、执行查询和更新操作。以下是一个使用Python的SQLite数据库库进行操作的示例:
import sqlite3
def update_dorm_info():
# 获取用户输入的宿舍信息
name = name_entry.get()
dorm = dorm_entry.get()
# 在这里实现宿舍信息的更新和保存到数据库的逻辑
# 连接数据库
conn = sqlite3.connect('student.db')
# 创建游标对象
cursor = conn.cursor()
# 执行更新语句
sql = "UPDATE students SET dorm = ? WHERE name = ?"
cursor.execute(sql, (dorm, name))
# 提交事务并关闭连接
conn.commit()
conn.close()
示例中使用了SQLite数据库,你可以根据实际情况选择其他类型的数据库。在实际使用中,你需要根据你的数据库结构和需要存储的信息来设计和执行相关的数据库操作。
这些是初步的解决方案,如果你有进一步的细化要求或者代码实现过程中遇到具体问题,请提供更多细节,我将尽力提供更详细的解答。