通讯录的实现

news2024/11/24 2:58:45

问天下谁与争锋,唯我傲视苍穹

此句赠与在看文章的你
该通讯录使用的语言是C语言,涉及的知识有动态开辟内存,和文件内存管理。
动态开辟内存是用来不断给通讯录增加容量的
文件管理是用来将通讯录的信息存储到文件里。
我会先从简单的写起,然后再渐渐完善。

第一个版本:静态通讯录(容量大小固定也不能将通讯录信息存储到文件里)
第二个版本:动态通讯录(容量大小可以随人员增多而增大,但还没有存储文件的能力)
第三个版本:相对完善的通讯录(动态的,可以文件存储)
三个版本基本框架一样只是为了提高功能而增加或修改了部分代码,所以第一个版本还是有必要仔细学习的。
还有这里将各个函数分装,将函数的声明放在头文件里面,函数的定义放在contact.c文件,函数的使用
放在test.c文件,将一些宏定义和结构体变量定义都发在头文件里面,当需要使用时,引用头文件#include "contact.h"即可。

通讯录的实现

  • 问天下谁与争锋,唯我傲视苍穹
  • 版本一:静态通讯录
          • 第一步:声明相关变量
          • 第二步:构思一个框架
          • 第三步:初始化结构体
          • 第四步:完善各分装函数
            • 初始化:
            • 1.增加联系人:
            • 2.删减联系人:
            • 3.查询联系人:
            • 4.修改联系人信息:
            • 5.显示联系人信息:
            • 6:排序-- 目前还没有写……
  • 版本二:动态通讯录
          • 第一步:对相关变量进行修改
          • 第二步:对相关函数进行修改
            • 1.修改初始化函数:
            • 2.修改添加函数
            • 3.释放空间
  • 版本三:相对完善通讯录
          • 第一步:掌握文件操作基本知识
          • 第二步:保存通讯录数据到文件里
          • 第三步:将文件的信息加载到通讯录里

版本一:静态通讯录

第一步:声明相关变量

定义一个结构体,用来表示人,里面存放着人的各种信息,比如姓名,年龄,性别,地址,电话等等信息。(用typedef 简化struct PoPinfo类型为PoPinfo)

typedef struct PoPinfo
{
	char name[NAME_MAX];
	int  age;
	char sex[SEX_MAX];
	char addr[ADD_MAX];
	char tele[TELE_MAX];
}PoPinfo;

再定义一个结构体,用来表示通讯录,里面存放着人的信息,还有通讯录里面的人数。
(用typedef将struct Contact类型简化为Contact类型)

typedef struct Contact
{
	PoPinfo data[MAX];//通讯录里人员的信息
	int sz;//当前已经放的信息的个数。
}Contact;

定义通讯录里面人员的信息容量比如姓名这个数组最多能容纳20个那用宏定义
#define NAME_MAX 20类似性别,地址,电话,通讯录人员个数等等。

#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 5
#define ADD_MAX 30
#define TELE_MAX 12
#define FIFIST 3
#define IND 2
第二步:构思一个框架

思考一下,你这个通讯录应该有什么功能呢
1.添加联系人的信息
2.删减联系人的信息
3.查询联系人的信息
4.修改联系人的信息
5.显示联系人的信息
6.排序联系人
……等等,有什么想法可以自己加入。
我们按照这个想法来构建,先用一个do while()内置一个菜单,按照选项来选择,所以还需要一个switch语句

void mneun()
{
	printf("**************************\n");
	printf("****1.add    2.del    ****\n");
	printf("****3.search 4.modify ****\n");
	printf("****5.show   6.sort   ****\n");
	printf("****0exit             ****\n");
	printf("**************************\n");
}
int main()
{
	Contact con;//创建一个结构体变量con;
	int input=0;
	do
	{
		mneun();//菜单--来选择
		printf("请输入要选择的:\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:  AddContact(&con);//添加联系人信息
			break;//将结构体的地址传过去
		case 2:DelContact(&con);//删减联系人信息
			break;
		case 3:SearchContact(&con);//查询联系人的信息
			break;
		case 4:ModiContact(&con); //修改联系人信息
			break;
		case 5:DisplayContact(&con);//显示联系人的信息
			break;
		case 6://排序联系人
			break;
		case 0:	printf("退出通讯录\n");
			break;
		default:break;
		}
	} while (input);
	return 0;
}
第三步:初始化结构体

在未传参之前要将结构体初始化
所以在创建结构体变量con后,就对它进行初始化
这里再分装成一个函数InitContact(&con);

int main()
{
	Contact con;//创建一个结构体变量con;
	//首先进行初始化这个结构体变量
	InitContact(&con);
	
	int input=0;
第四步:完善各分装函数
Contact.h头文件里面进行函数的声明
//函数声明
void InitContact(Contact* pc);//初始化通讯录
void AddContact(Contact* pc);//添加联系人信息
void DelContact(Contact* pc);//删减联系人信息
void SearchContact(Contact* pc);//查询联系人信息
void DisplayContact(Contact* pc);//显示联系人信息
void ModiContact(Contact* pc);//修改联系人信息

2.函数的定义(函数功能的实现)

初始化:
//初始化结构体
//将结构体的地址传过去,用指针来接收(合理吧)
void InitContact(Contact* pc)
{
	assert(pc);//首先断言确保pc不为NULL
	pc->sz = 0;//初始化为0
	memset(pc->data, 0, sizeof(pc->data));//用memset函数将data数据初始化为0.
}

1.增加联系人:
//添加ADD函数
void AddContact(Contact* pc)
{
	assert(pc);//首先断言确保pc不为指针
	if (pc->sz == MAX)//每次都要判断一下是否通讯录人员已经满了无法添加了
	{
		printf("通讯录已满,无法添加\n");
		return;
	}
	//增加一个人的信息
	printf("请输入姓名:\n");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:\n");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("请输入性别:\n");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入地址:\n");
	scanf("%s", pc->data[pc->sz].addr);
	printf("请输入电话:\n");
	scanf("%s", pc->data[pc->sz].tele);
	pc->sz++;//增加一个人的信息,人数就加1
}
2.删减联系人:
//查人名字函数
int FindByName(Contact* pc, char name[])
{
	int i;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			return i;
		}
		
	}
	return -1;
}
//删减联系人信息
void DelContact(Contact* pc)//
{
	assert(pc);//断言判断
	char name[NAME_MAX] = { 0 };
	if (pc->sz == 0)//也要判断一下通讯录人员是否为0,为0 删个毛线呀
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	//要删除指定的人;
	//就先找到指定的人
	printf("请输入要删除的联系人名字:\n");
	scanf("%s", name);
	int ret=FindByName(pc, name);//再分装一个查人姓名函数,所以将要删除的名字和pc传过去
	if (ret == -1)
	{
		printf("删除的人不存在\n");
		return;
	}
	else
	{
		int i;
		for (i = ret; i < pc->sz - 1; i++)
		{
			pc->data[i] = pc->data[i + 1];//将后面的信息往前覆盖,那这个信息就没有了,相当于被删掉了
		}
		pc->sz--;//删掉一个人数要减1
		printf("删除成功\n");
	}
	return;


}
3.查询联系人:
void SearchContact(Contact* pc)
{
	assert(pc);//断言判断
	if (pc->sz == 0)//再判断一下是否还有人
	{
		printf("通讯录为空无法查询\n");
	}
	char name[NAME_MAX];
	printf("请输入要查找的联系人名字:\n");
	scanf("%s", name);
	int pos=FindByName(pc, name);//查询信息要先找到这个人,根据查人名字函数来查找
	if (pos == -1)
	{
		printf("抱歉,查找的人不存在\n");
		return;
	}
	else
	{//打印查找人的信息
		printf("%s\n", pc->data[pos].name);
		printf("%d\n", pc->data[pos].age);
		printf("%s\n", pc->data[pos].sex);
		printf("%s\n", pc->data[pos].addr);
		printf("%s\n", pc->data[pos].tele);
	}
}
4.修改联系人信息:

发没发现删减,查询,修改,关键都是要找到要查找的目标,所以我们的查人名字函数还是很好用的,嘿嘿。

void ModiContact(Contact* pc)//修改联系人操作
{
	assert(pc);断言
	char name[NAME_MAX];
	printf("请输入要修改联系人名字:\n");
	scanf("%s", name);
	int pos = FindByName(pc, name);//查到这个名字,查到后就把下标返回过来
	if (pos == -1)
	{
		printf("抱歉,该联系人不存在\n");
		return;
	}
	else//如果有这个人,就把他当前的信息全部修改掉,通过返回的下标来修改这个人的信息。
	{
		printf("请输入姓名:\n");
		scanf("%s", pc->data[pos].name);
		printf("请输入年龄:\n");
		scanf("%d", &(pc->data[pos].age));
		printf("请输入性别:\n");
		scanf("%s", pc->data[pos].sex);
		printf("请输入地址:\n");
		scanf("%s", pc->data[pos].addr);
		printf("请输入电话:\n");
		scanf("%s", pc->data[pos].tele);
		printf("修改完毕\n");
	}
}
5.显示联系人信息:
void DisplayContact(Contact* pc)
{
	assert(pc);//断言
	if (pc->sz == 0)//判断一下
	{
		printf("通讯录中没有人员可显示\n");
		return;
	}
	int i = 0;
	printf("%-10s\t%-4s\t%-4s\t%-10s\t%-12s\n", "姓名", "年龄", "性别", "地址", "电话");//这个是把列表打印出来然后下面人员的信息都要按照这里的格式书写,这样就可以美观一致了。都是左对齐。
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-10s\t%-4d\t%-4s\t%-10s\t%-12s\n",
			                            pc->data[i].name,
										pc->data[i].age,
										pc->data[i].sex,
										pc->data[i].addr,
										pc->data[i].tele);
	}
}
6:排序-- 目前还没有写……

好了差不多版本一已经完成。我们来看看成果吧:
1.添加
在这里插入图片描述
2.删除在这里插入图片描述

3.查询
在这里插入图片描述
4.修改
在这里插入图片描述
在这里插入图片描述

版本二:动态通讯录

第一步:对相关变量进行修改

第二版本的通讯录具有动态特性,也就是可以随着联系人的增多存储空间也增大。
那就不需要对data这个数组进行限制,直接使用指针,我们再定义一个变量capacity为/第一次存放人员的最大存储量,等每次超过这个最大存储量就自动给它增容,假设capacity 为3,每次增容为2个人员
利用宏定义#define FIFIST 3,#define IND 2
所以通讯录的结构体代码就改成这样:

typedef struct Contact
{
	PoPinfo *data;//指向通讯录里人员信息的空间
	int sz;//当前已经放的信息的个数。
	int capacity;//第一次存放人员的最大存储量
}Contact;
第二步:对相关函数进行修改
1.修改初始化函数:

既然没有用数组来申请空间,那我们只能自己动态开辟空间给指针data,开辟的空间就是一开始的capacity存储量3个人员,然后再将sz置0,capacity置成3

void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	PoPinfo* ptr = (PoPinfo*)calloc(FIFIST, sizeof(PoPinfo));//动态开辟空间,并初始化为0
	if (ptr == NULL)//每次动态开辟完空间都要检查一下,防止开辟失败
	{
		perror("InitContact:calloc");//开辟失败就将失败的信息打印出来
		return;//结束
	}
	pc->data = ptr;//为data开辟了3个联系人大小的空间,所以一开始通讯录拥有3个存储空间
	pc->capacity = FIFIST;//3个人员
}
2.修改添加函数

你想呀,添加函数肯定要修改的哇,当通讯录的人数添加到超过capacity(最初的容纳量)时就要进行增容呀,所以要对添加函数这里加一个增容函数,我们把这个增容操作,也分装成一个函数,后面还要用到,嘿嘿

//增容函数
void Check_capacity(Contact* pc)
{
	if (pc->sz == pc->capacity)//如果人员数到达capacity就说明满了要增容
	{
		//需要增容
		//动态增容,每次增容在原capacity容量下增加 IND个
		PoPinfo* ptr = (PoPinfo*)realloc(pc->data, (pc->capacity + IND) * sizeof(PoPinfo));
		if (ptr == NULL)//检查一下是否为NULL
		{
			perror("Check_capacity::realloc");//如果错误,把错误信息返回
		}
		pc->data = ptr;
		pc->capacity += IND;//每增容一次,capacity要加上2
		printf("增容成功\n");
	}
}
void AddContact(Contact* pc)
{
	assert(pc);
	//进入函数首先要进行判断是否有容量存储。
	Check_capacity(pc);//增容函数。
	
	//增加一个人的信息
	printf("请输入姓名:\n");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:\n");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("请输入性别:\n");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入地址:\n");
	scanf("%s", pc->data[pc->sz].addr);
	printf("请输入电话:\n");
	scanf("%s", pc->data[pc->sz].tele);
	pc->sz++;
}
3.释放空间

其他函数基本不用改,照样能运行起来,只不过我们不要忘记,动态开辟空间后,用完一定要释放空间
不然会造成内存泄漏,会出bug的,所以我们在退出通讯录的时候记得要将开辟的空间释放掉,并将不用的指针置为NULL,防止变成野指针,所以我们在退出之前再分装一个函数,专门用来销毁空间的。
记得要到头文件里面去声明一下才能使用哟,

void Destroy_contact(Contact* pc)
{
	free(pc->data);//释放空间
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
	pc = NULL;

int input=0;
	do
	{
		mneun();
		printf("请输入要选择的:\n");
		scanf("%d", &input);
		switch (input)
		{//第二步增加ADD函数,增加联系人
		case 1:  AddContact(&con);
			break;
		case 2:DelContact(&con);//第三步添加删减函数,删减联系人
			break;
		case 3:SearchContact(&con);//寻找联系人
			break;
		case 4:ModiContact(&con); //修改联系人信息
			break;
		case 5:DisplayContact(&con);//显示联系人
			break;
		case 6:   //sort()
			break;
		case 0:	printf("退出通讯录\n");
			Destroy_contact(&con);//销毁空间
			break;
		default:break;
		}
	} while (input);

好了,到这里,第二版本的动态通讯录也基本完成啦,让我们来看看成果吧:
在这里插入图片描述
首先我直接先给这个通讯录添加了3个联系人
再添加一位看看会怎么样:

在这里插入图片描述
发现可以自动增容,嘿嘿,每次增容增加两个数量让我再测测:
在这里插入图片描述
果然当添加到第6个的时候又开始增容了,所以这个版本弄好啦。你学会了嘛?
学会了就再学下面的完善版本吧

版本三:相对完善通讯录

第一步:掌握文件操作基本知识

写了通讯录的程序,当通讯录运行起来的时候,可以给通讯录中增加、删除数
据,此时数据是存放在内存中,当程序退出的时候,通讯录中的数据自然就不存在了,等下次运行通讯
录程序的时候,数据又得重新录入,如果使用这样的通讯录就很难受
这个版本我们需要做的是使用文件管理操作等知识让通讯录的信息存储到文件中来。还需要将文件里的信息再加载到通讯录里面去。使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。这里我们得了解文件的相关知识才能动手操作。

  1. 文件在读写之前应该先打开文件,在使用结束之后应该关闭文件
  2. 在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,就相当于建立了指针和文件的关系
  3. ANSIC规定使用fopen函数来打开文件,Fclose来关闭文件

//打开文件
FILE*fopen(const char*filename,const char *mode)
//关闭文件
int flcose(FILE*stream)

打开方式如下:
在这里插入图片描述
如果要将内存数据写入到文件里面请用 fwrite的喔
将屏幕上的信息输出到文件里面
如果要将文件的数据加载到电脑上请用fread的喔
将文件的信息输入到电脑里
在这里插入图片描述

好了基本知识理解后开整:

第二步:保存通讯录数据到文件里

我们想哈,我们写进去的数据原先是在内存里面保存着,然后退出通讯录后,数据就不存在了,我们学完文件操作后就能在退出通讯录之前将这些数据存到文件里面去。
所以在退出通讯录前,搞一个保存数据的函数,这个函数要在空间销毁之前哈,我原先就把这个保存数据的函数放在销毁空间函数的后面然后死活得不到通讯录里面的信息,,呜呜呜。所以你们可不能犯这样的错误。

int input=0;
	do
	{
		mneun();
		printf("请输入要选择的:\n");
		scanf("%d", &input);
		switch (input)
		{//第二步增加ADD函数,增加联系人
		case 1:  AddContact(&con);
			break;
		case 2:DelContact(&con);	//第三步添加删减函数,删减联系人
			break;
		case 3:SearchContact(&con);//寻找联系人信息
			break;
		case 4:ModiContact(&con); //修改联系人信息
			break;
		case 5:DisplayContact(&con);//显示联系人信息
			break;
		case 6:
			break;
		case 0:	printf("退出通讯录\n");
			Protect_contact(&con);//将通讯录的数据信息存到文件里面去,要放在销毁空间函数的前面哈,,  //退出之前要将信息存储到文件里
			Destroy_contact(&con);//销毁空间
			break;
		default:break;
		}
	} while (input);
void Protect_contact(Contact* pc)//保存数据到文件
{
	//写数据之前要
	//打开文件
	FILE* pf = fopen("contact.txt", "wb");//打开方式为"wb"
	 //为了读和写,新建一个新的二进制文件
	if (pf == NULL)//检查一下是否文件打开失败
	{
		perror("Protect_contact::fopen");//如果失败将失败信息返回
		
	}
	{
		//写数据,将pc所指向的data的人员信息写入pf开辟的文件里面去,空间大小    为sizeof(PoPinfo),一次传一个数据。
		int i = 0;
		for (i = 0; i < pc->sz; i++)
		{
			fwrite(pc->data + i, sizeof(PoPinfo), 1, pf);
		}
		fclose(pf);
		pf = NULL;
		printf("保存数据成功\n");
	}

}

fwrite如何使用请看下图喔:
在这里插入图片描述
fopen的使用:
在这里插入图片描述

第三步:将文件的信息加载到通讯录里

我们再想哈,如果我们第一次用通讯录添加了几个联系人信息,然后退出通讯录,这些信息存在一个文件里面,那下次再打开这个通讯录按道理来讲这些信息一个还在通讯录里面,所以每次打开通讯录都要将上次的文件信息加载到通讯录里面。
那我们每次进入通讯录是不是都要初始化下,我们应该将这个加载数据的函数放在初始化函数里面来,一上来就把上次的数据加载进来。

void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	PoPinfo* ptr = (PoPinfo*)calloc(FIFIST, sizeof(PoPinfo));//动态开辟空间,并初始化为0
	if (ptr == NULL)//每次动态开辟完空间都要检查一下,防止开辟失败
	{
		perror("InitContact:calloc");//开辟失败就将失败的信息打印出来
		return;//结束
	}
	pc->data = ptr;
	pc->capacity = FIFIST;//3个人员
	LoadContact(pc);//加载数据函数
	//将文件信息加载到pc所指向的空间里面去也就是通讯录
}

怎么将文件信息加载到通讯录里面呢?
这时我们需要用 fread函数,将文件信息输入到电脑里
那是不是直接将文件里的数据全部加载到通讯录里面呢?弄个循环有多少人员信息就循环多少次呢?
当然不行啦
你们想一下,我们的通讯录一开始的容量是有限制的虽然能增容但一开始的容纳量就只有FIFIST (3个),一旦超过这个数量
那通讯录就没有容纳量存储了,就需要增容来接收文件里的数据。
所以在接收文件里的数据之前先判断一下是否要增容呀,上面我们已经将增容操作写成函数啦,嘿嘿,所以直接拿过来放在这里

void LoadContact(Contact* pc)
{
	//将文件信息加载到通讯录里面
	//读数据
	//1..打开文件
	FILE* pf = fopen("contact.txt", "rb");//以rb的方式打开文件
	if (pf == NULL)//判断一下是否打开文件成功
	{
		perror("LoadContatc::fopen");
	}
	else
	{
		//2.读数据了
		//因为一开始不知道文件里面有多少数据呀,我们得把文件里面的数据全部拿出来
		//但因为一开始的通讯录容纳量有限制,所以一旦文件里面的数据超过一开始给定的容纳量就需要
		//增容。
		PoPinfo tmp = { 0 };
		int i = 0;
		while (fread(&tmp, sizeof(PoPinfo), 1, pf))
		{
			//首先考虑扩容问题;
			Check_capacity(pc);
			pc->data[i] = tmp;//将读取的数据放入通讯录里
			pc->sz++;//放进去一个sz加1
			i++;
		}
		fclose(pf);//关闭文件
		pf = NULL;
	}
}

fread函数如何使用:
在这里插入图片描述

还有前面函数都需要声明哈,有的我没说我直接给你们看下代码,不要漏掉了
函数的声明:

void InitContact(Contact* pc);//初始化通讯录
void Destroy_contact(Contact* pc);//销毁通讯录空间
void AddContact(Contact* pc);//添加联系人信息
void DelContact(Contact* pc);//删减联系人信息
void SearchContact(Contact* pc);//查询联系人信息
void DisplayContact(Contact* pc);//显示联系人信息
void ModiContact(Contact* pc);//修改联系人信息
void Protect_contact(Contact* pc);//将通讯录信息存储到文件里面去
void LoadContact(Contact* pc);//将文件信息加载到通讯录里面去

代码在Github上:https://github.com/ITwei6/-

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/169786.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

公司裁员70%,小组从20个人降到5个人,年底公司耍无赖,全员打绩效C,就为了不发年终奖!...

年终奖写进合同&#xff0c;公司还能耍赖不给吗&#xff1f;一位网友吐槽&#xff1a;坐标小公司&#xff0c;公司裁员70%&#xff0c;自己组从20个人降到5个人。现在年底了&#xff0c;公司耍无赖&#xff0c;全员打绩效C&#xff0c;就为了不发年终奖&#xff01;年终奖都是写…

454. 四数相加 II 383. 赎金信 15. 三数之和 18. 四数之和

454. 四数相加 II 巧用哈希表&#xff0c;哈希表键值对对应的是两数之和&#xff0c;两数之和出现次数。 首先定义 一个unordered_map&#xff0c;key放a和b两数之和&#xff0c;value 放a和b两数之和出现的次数。遍历大A和大B数组&#xff0c;统计两个数组元素之和&#xff0…

Nginx与LUA(4)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e;Nginx既然可以限制流量&#xff0c;那能不能「扩展」流量呢&#xff1f;当然可以&#xff0c;但可能不是你想象的那种「扩展」&#xff0c;更准确地来说是复制&am…

KVM虚拟化基本操作

1&#xff0c;虚拟化的一些介绍 虚拟化软件是可以让一台物理主机建立与执行一至多个虚拟化环境的软件&#xff0c;虚拟化将硬件、操作系统和应用程序一同封装一个可迁移的虚拟机档案文件中。 安装位置分类 目前从Hypervisor(虚拟机管理程序)安装位置分类&#xff0c;虚拟化层…

[linux] 进程相关概念理解

文章目录1. 什么是进程管理本质的解释描述组织结论2.查看进程查看进程方法1创建终端输入命令显示进程一个程序存在多个进程查看进程方法2查看成功查看失败结论3.通过系统调用获取进程标识符1.获取PID值验证PID值是否正确2. 获取父进程PID值验证3. 父进程为什么不变化&#xff1…

Vue3实现九宫格抽奖效果

前言 好久没有写文章了&#xff0c;上一次发文还是年终总结&#xff0c;眨眼间又是一年&#xff0c;每每想多总结却是坚持不来&#xff0c;难顶。  这次分享一个九宫格抽奖小游戏&#xff0c;缘起是最近公司内部做积分抽奖需求&#xff0c;抽出其中抽奖动效做一个总结&#x…

利用steam搬砖信息差项目,投入不到1万,一个月净赚3万+

老实说&#xff1a;我在做之前没有任何经验&#xff0c;但做梦也没想到&#xff0c;刚开始操作收益就远远超出我的预期&#xff01; 这个账号我才运营了一个月左右&#xff0c;就有3万多的销售额。现在我每月的收入都在上万元&#xff0c;而且随着收益越来越多&#xff0c;操作…

亚马逊云科技re:Invent引领云计算未来方向

亚马逊云科技合作伙伴网络大使计划&#xff0c;吸纳拥有多项亚马逊云科技认证和深入亚马逊云科技知识的合作伙伴成员&#xff0c;协助其成为各个领域的技术专家&#xff0c;开发可供公众使用的内容&#xff0c;如技术写作、博客、开源项目&#xff0c;宣传亚马逊云科技及其合作…

JavaWeb基础(四) JSP介绍

JavaWeb基础(四) JSP介绍 1&#xff0c;JSP 概述 JSP&#xff08;全称&#xff1a;Java Server Pages&#xff09;&#xff1a;Java 服务端页面。是一种动态的网页技术&#xff0c;其中既可以定义 HTML、JS、CSS等静态内容&#xff0c;还可以定义 Java代码的动态内容&#xf…

.shp文件的存储结构是怎样的?底层读取shapefile文件

.shp文件的存储结构是怎样的&#xff1f;底层读取shapefile文件基础知识shp的存储结构python 字节流读取Shp文件基础知识 大家都比较熟悉shp文件&#xff0c;它是GIS软件可以读取的矢量文件。但是大家知道它的存储结构吗&#xff1f;这次带着大家聊聊shp文件的存储结构&#x…

Linux diff 命令

Linux diff 命令用于比较文件的差异。diff 以逐行的方式&#xff0c;比较文本文件的异同处。如果指定要比较目录&#xff0c;则 diff 会比较目录中相同文件名的文件&#xff0c;但不会比较其中子目录。语法diff [-abBcdefHilnNpPqrstTuvwy][-<行数>][-C <行数>][-D…

【Java】【系列篇】【Spring源码解析】【三】【体系】【Resource体系】

主要用于加载配置资源等等Resource 前提须知 ClassLoader类的getResource和getResourceAsStream方法是原生JDK中内置的资源加载文件的方式&#xff1b;Spring中资源模型顶级接口不是Resource&#xff0c;而是InputStreamSource接口&#xff1b;Spring为何自己实现一套资源加载…

Nessus 扫描web服务

系列文章 Nessus介绍与安装 Nessus Host Discovery Nessus 高级扫描 Nessus 扫描web服务 1.启动nessus cd nessus sh qd_nessus.sh2.进入nessus网站 https://192.168.3.47:8834/3.点击【New Scan】 4.点击【Web应用程序测试】 5.输入name【web扫描】&#xff0c;描述【web…

Lesson 2. 矩阵运算基础、矩阵求导与最小二乘法

文章目录一、NumPy 矩阵运算基础1. NumPy 中的矩阵表示2. NumPy 中特殊矩阵构造方法3. NumPy 中矩阵基本运算4. NumPy 中矩阵代数运算二、矩阵方程与向量求导方法1. 方程组求解与矩阵方程求解2. 向量求导运算2.1 向量求导基本方法2.2 常见向量求导公式三、最小二乘法的推导过程…

Vue3 函数式组件的开发方式

声明式组件和服务式组件 无论是使用第三方组件库&#xff0c;还是自己封装组件&#xff0c;有一类组件有些与众不同&#xff0c;那就是函数式/服务式组件&#xff0c;比如 Message 消息组件、Notification 通知组件、Loading 加载组件等等。 以 ElementPlus 组件库为例&#…

.net反序列化新手入门--Json.Net

**01 **Json.net简介 Json.net即Newtonsoft.Json&#xff0c;是.Net中开源的Json序列化和反序列化工具&#xff0c;官方地址&#xff1a;http://www.newtonsoft.com/json。 它虽然不是官方库&#xff0c;但凭借其优秀的性能获得了广大开发者的喜爱。 官网给出的性能比较&…

8大预测分析工具比较

什么是预测分析工具&#xff1f; 预测分析工具融合了人工智能和业务报告。这些工具包括用于从整个企业收集数据的复杂管道&#xff0c;添加统计分析和机器学习层以对未来进行预测&#xff0c;并将这些见解提炼成有用的摘要&#xff0c;以便业务用户可以对此采取行动。 预测的…

day17集合

1.Set集合 1.1Set集合概述和特点【应用】 不可以存储重复元素没有索引,不能使用普通for循环遍历 1.2Set集合的使用【应用】 存储字符串并遍历 public class MySet1 {public static void main(String[] args) {//创建集合对象Set<String> set new TreeSet<>()…

超级详细的几道python题(附答案)含解析、建议收藏

名字&#xff1a;阿玥的小东东 学习&#xff1a;python、正在学习c 主页&#xff1a;阿玥的小东东 目录 判断字符串 a “welcome to my world” 是否包含单词 b “world”&#xff0c;包含返回 True&#xff0c;不包含返回 False。 从 0 开始计数&#xff0c;输出指定字符串…

SSH使用入门

目录 .1 基础配置 1.1 vscode使用 1.2 HOST连接 .2 文件传输 .1 基础配置 1.1 vscode使用 拓展里搜索 然后点击remote里的设置 选择配置 然后填写配置 Hostname是你要ssh的服务器的ip地址 user是你要连接的服务器的用户名 Host可以随便写一个 如果有端口号也要对应修改 …