C语言通讯录

news2024/11/23 8:04:30

        在本博客中,我们将介绍如何使用C语言构建一个基本的通讯录。主要涉及C语言的指针、结构体、动态内存管理、文件操作等方面的知识。我们还将学习如何使用C语言的各种功能和技巧来实现通讯录的各种操作,如添加联系人、编辑联系人、删除联系人和搜索联系人等,并且还会对通讯录进行多个版本的优化。

        无论您是初学者还是有一定编程经验的开发者,通过学习和实践C语言通讯录的构建,你都能够加深对C语言的理解,并提升自己的编程能力。这部分知识的学习和代码实践对后面学习数据结构也有很大帮助。希望你能够通过本博客的学习,掌握C语言通讯录的开发技巧,并能够在实际项目中灵活应用。

        让我们开始学习并构建自己的C语言通讯录吧!

​​​​​​​

 

目录

🌹零、前置知识复习: 

🌸1.结构体:

🌸2.动态内存管理

🌸3.文件操作

🌸4.分文件编写:

🌹一、版本一:静态通讯录

🌸1.结构体初始化

🌸2.添加联系人

🌸3.显示联系人

🌸4.删除联系人

🌸5.查找联系人

🌸6.修改联系人

🌸7.排序

🌹二、版本二:动态顺序表

🌸1.结构体初始化

🌸2.添加联系人

🌸3.销毁通讯录

🌹三、版本三:通讯录与文件

🌸1.通讯录读取文件信息

🌸2.通讯录信息保存在文件

附录:通讯录最终版本代码


🌹零、前置知识复习: 

🌸1.结构体:

详见博客:结构体

通过一个结构体来简单定义联系人信息:

#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30

typedef struct PeoInfo
{
	char name[MAX_NAME];//姓名
	int age;//年龄
	char sex[MAX_SEX];//性别
	char tele[MAX_TELE];//电话
	char addr[MAX_ADDR];//住址
}PeoInfo;

 结构体传参:只能传结构体地址

原因:

  • 函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。 如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。
  • 传参时,形参是实参的拷贝,我们要改变实参,只能传递指针。

🌸2.动态内存管理

malloc,realloc等函数的使用,尤其是realloc

详见博客:动态内存管理

🌸3.文件操作

fwite和fread函数的使用

详见博客:文件操作

🌸4.分文件编写:

Contact.h 主要写类型和函数的声明

Contact.c 主要写函数的实现

test.c 通讯录测试

详见博客:文件包含相关博客


具体细节下面介绍:

🌹一、版本一:静态通讯录

这个版本的通讯录是通过大小指定的数组来存储联系人信息。

静态通讯录结构体的实现如下:

//静态版本
typedef struct Contact
{
	PeoInfo data[MAX];
	int sz;
}Contact;

这里我们的Contact结构体中存放的是PeoInfo类型的数组data,并且大小固定为MAX,超出容量后就不能添加联系人了。这也是静态通讯录的一个缺陷。sz是联系人个数。

下面详解各种功能的实现:

🌸1.结构体初始化

//静态版本
void InitContact(Contact* pc)
{
	assert(pc);
	memset(pc->data, 0, sizeof(pc->data));
	pc->sz = 0;
}

使用memset内存函数进行初始化

memset函数使用详见:内存操作函数

🌸2.添加联系人

//静态版本

void AddContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == MAX)
	{
		printf("通讯录已满,无法添加\n");
		return;
	}

	printf("请输入名字:>");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->sz].addr);

	pc->sz++;
	printf("成功增加联系人\n");
}

🌸3.显示联系人

void ShowContact(const Contact* pc)
{
	assert(pc);

	int i = 0;
	printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");//预留一定空间,并且左对齐
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-10s\t%-4d\t%-5s\t%-12s\t%-30s\n", 
			pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].addr);
	}
}

🌸4.删除联系人

删除联系人之前,我们要查找联系人。不管删除需要查找,我们修改联系人也需要查找。所以这里我们可以封装一个按姓名查找的函数。

static int FindByName(const Contact* pc, char name[])
//static修饰函数,只能在该源文件中使用
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			return i;//找到了
		}
	}
	return -1;//没找到
}

注意:

  • 函数用static修饰,表示函数由外部链接属性转变为内部链接属性。只能在本源文件中使用。
  • 字符串的比较,要用strcmp函数。

 删除联系人函数:

//静态
void DelContact(Contact* pc)
{
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	char name[MAX_NAME] = {0};
	assert(pc);

	//查找
	printf("请输入要删除的人名字:>");
	scanf("%s", name);
    int del = FindByName(pc,name);
    if(del == -1)
    {
        printf("要删除的人不存在!\n");
        return;
    }

    //删除
    int i=0;
	for (i = del; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;

	printf("成功删除联系人\n");
}

🌸5.查找联系人

void SearchContact(const Contact* pc)
{
	assert(pc);

	char name[MAX_NAME] = { 0 };
	printf("请输入要查找人的名字:>");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要查找的人不存在\n");
	}
	else
	{
		printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");
		//打印数据
		printf("%-10s\t%-4d\t%-5s\t%-12s\t%-30s\n",
			pc->data[pos].name,
			pc->data[pos].age,
			pc->data[pos].sex,
			pc->data[pos].tele,
			pc->data[pos].addr);
	}
}

🌸6.修改联系人

void ModifyContact(Contact* pc)
{
	assert(pc);

	char name[MAX_NAME] = { 0 };
	printf("请输入要修改人的名字:>");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
		printf("要修改的人不存在\n");
	else
	{
		printf("请输入名字:>");
		scanf("%s", pc->data[pos].name);
		printf("请输入年龄:>");
		scanf("%d", &(pc->data[pos].age));
		printf("请输入性别:>");
		scanf("%s", pc->data[pos].sex);
		printf("请输入电话:>");
		scanf("%s", pc->data[pos].tele);
		printf("请输入地址:>");
		scanf("%s", pc->data[pos].addr);

		printf("修改成功\n");
	}
}

🌸7.排序

复习:qsort函数的使用

int CmpByName(void* p1, void* p2)
{
	return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}

int CmpByAge(void* p1, void* p2)
{
	return ((PeoInfo*)p1)->age - ((PeoInfo*)p2)->age;
}

void SortContact(Contact* pc) 
{
	int option = 0;
	printf("****1.姓名   2.年龄****\n");
	printf("按着姓名还是年龄排序?>");
	scanf("%d", &option);
	if (option == 1)
	{
		qsort(pc->data, pc->sz, sizeof(PeoInfo), CmpByName); 
	}
	else
	{
		qsort(pc->data, pc->sz, sizeof(PeoInfo), CmpByAge);
	}
}

🌹二、版本二:动态顺序表

上面我们说过,静态顺序表只用一个数组来存储联系人信息,满了之后就不能扩容,这是一个很大的缺陷,下面我们通过动态内存管理来实现动态通讯录。

动态通讯录结构体如下:

#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30
#define DEFAULT_SZ 3//初始容量
#define INC_SZ 2//扩容添加容量

//动态版本
typedef struct Contact
{
	PeoInfo* data;
	int sz;
	int capacity;
}Contact;

PeoInfo指针用来维护动态开辟的空间。sz是联系人数量。capacity是通讯录容量。 

与静态版本相比,我们只需修改初始化、添加函数,并添加一些其他函数。

🌸1.结构体初始化

//动态版本
void InitContact(Contact* pc)
{
	assert(pc);
	pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));
	if (!pc->data)
	{
		perror("InitContact");
		exit(-1);
	}
	pc->sz = 0;
	pc->capacity = DEFAULT_SZ;

}

🌸2.添加联系人

如果容量满了,就可以使用realloc函数进行扩容,我们可以把扩容函数进行封装。

realloc函数的使用细节,详见:动态内存管理

static int CheckCapacity(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		PeoInfo* tmp = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
		if (!tmp)
		{
			perror("CheckCapacity");
			return 0;
		}
		
	    pc->data = tmp;
		pc->capacity += INC_SZ;
		printf("增容成功\n");
		return 1;
		
	}
	return 1;
}

添加联系人:

//动态版本
void AddContact(Contact* pc)
{
	assert(pc);

	if (CheckCapacity(pc) == 0)
	{
		exit(-1);
	}

	printf("请输入名字:>");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:>"); 
	scanf("%d", &(pc->data[pc->sz].age)); 
	printf("请输入性别:>"); 
	scanf("%s", pc->data[pc->sz].sex); 
	printf("请输入电话:>"); 
	scanf("%s", pc->data[pc->sz].tele); 
	printf("请输入地址:>"); 
	scanf("%s", pc->data[pc->sz].addr);

	pc->sz++; 
	printf("成功增加联系人\n");
}

🌸3.销毁通讯录

自己开辟的空间用完后要销毁,我们封装成Destroy函数。

void DestroyContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
}

🌹三、版本三:通讯录与文件

我们发现,以上两个版本都是在内存中保存信息,退出程序后通讯录信息就会丢失,无法做到数据持久化,这里就能用到文件了。

🌸1.通讯录读取文件信息

static void LoadContact(Contact* pc)
{
	FILE* pf = fopen("data.txt", "rb");
	if (!pf)
	{
		perror("fopen");
		exit(-1);
	}

	PeoInfo tmp = { 0 };
	while (fread(&tmp, sizeof(PeoInfo), 1, pf))
	{
		if (!CheckCapacity(pc))
		{
			exit(-1);
		}
		pc->data[pc->sz] = tmp;
		pc->sz++;
	}

	fclose(pf);
	pf == NULL;
}

注意:

static修饰,只在本源文件使用

二进制读写函数的返回值

详见:文件操作

🌸2.通讯录信息保存在文件

void SaveContact(Contact* pc)
{
	FILE* pf = fopen("data.txt", "wb");
	if (!pf)
	{
		perror("fopen");
		exit(-1);
	}

	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
	}

	fclose(pf);
	pf = NULL;
}

附录:通讯录最终版本代码

#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>

#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30
#define DEFAULT_SZ 3
#define INC_SZ 2

enum OPTION
{
	EXIT,//0
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT
};

typedef struct PeoInfo
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
}PeoInfo;

//通讯录

静态版本
//typedef struct Contact
//{
//	PeoInfo data[MAX];
//	int sz;
//}Contact;

//动态版本
typedef struct Contact
{
	PeoInfo* data;
	int sz;
	int capacity;
}Contact;

//初始化通讯录
void InitContact(Contact* pc);

//增加联系人
void AddContact(Contact* pc);

//显示所有联系人的信息
void ShowContact(const Contact* pc);

//删除指定联系人
void DelContact(Contact* pc);

//查找指定联系人
void SearchContact(const Contact* pc);

//修改指定联系人
void ModifyContact(Contact* pc);

//排序
void SortContact(Contact* pc);

//通讯录销毁
void DestroyContact(Contact* pc);

//保存通讯录信息到文件中
void SaveContact(Contact* pc);
#define _CRT_SECURE_NO_WARNINGS 1
#include "Contact.h"

static void LoadContact(Contact* pc)
{
	FILE* pf = fopen("data.txt", "rb");
	if (!pf)
	{
		perror("fopen");
		return;
	}

	PeoInfo tmp = { 0 };
	while (fread(&tmp, sizeof(PeoInfo), 1, pf))
	{
		if (!CheckCapacity(pc))
		{
			return;
		}
		pc->data[pc->sz] = tmp;
		pc->sz++;
	}

	fclose(pf);
	pf == NULL;
}

静态版本
//void InitContact(Contact* pc)
//{
//	assert(pc);
//	memset(pc->data, 0, sizeof(pc->data));
//	pc->sz = 0;
//}

//动态版本
void InitContact(Contact* pc)
{
	assert(pc);
	pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));
	if (!pc->data)
	{
		perror("InitContact");
		return;
	}
	pc->sz = 0;
	pc->capacity = DEFAULT_SZ;

	//文件信息输入到通讯录中
	LoadContact(pc); 
}

静态版本
//
//void AddContact(Contact* pc)
//{
//	assert(pc);
//	if (pc->sz == MAX)
//	{
//		printf("通讯录已满,无法添加\n");
//		return;
//	}
//
//	printf("请输入名字:>");
//	scanf("%s", pc->data[pc->sz].name);
//	printf("请输入年龄:>");
//	scanf("%d", &(pc->data[pc->sz].age));
//	printf("请输入性别:>");
//	scanf("%s", pc->data[pc->sz].sex);
//	printf("请输入电话:>");
//	scanf("%s", pc->data[pc->sz].tele);
//	printf("请输入地址:>");
//	scanf("%s", pc->data[pc->sz].addr);
//
//	pc->sz++;
//	printf("成功增加联系人\n");
//}


static int CheckCapacity(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		PeoInfo* tmp = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
		if (!tmp)
		{
			perror("CheckCapacity");
			return 0;
		}
		else
		{
			pc->data = tmp;
			pc->capacity += INC_SZ;
			printf("增容成功\n");
			return 1;
		}
	}
	return 1;
}

//动态版本
//静态版本有一个致命缺点,就是满了就不能添加了,很明显这种版本是会被淘汰的,所以我们使用动态内存管理的方式开辟空间
void AddContact(Contact* pc)
{
	assert(pc);

	if (CheckCapacity(pc) == 0)
	{
		return;
	}

	printf("请输入名字:>");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:>"); 
	scanf("%d", &(pc->data[pc->sz].age)); 
	printf("请输入性别:>"); 
	scanf("%s", pc->data[pc->sz].sex); 
	printf("请输入电话:>"); 
	scanf("%s", pc->data[pc->sz].tele); 
	printf("请输入地址:>"); 
	scanf("%s", pc->data[pc->sz].addr);

	pc->sz++; 
	printf("成功增加联系人\n");
}

void ShowContact(const Contact* pc)
{
	assert(pc);

	int i = 0;
	printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");//预留一定空间,并且左对齐
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-10s\t%-4d\t%-5s\t%-12s\t%-30s\n", 
			pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].addr);
	}
}

static int FindByName(const Contact* pc, char name[])
//static修饰函数,只能在该源文件中使用
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			return i;//找到了
		}
	}
	return -1;//没找到
}

静态
//void DelContact(Contact* pc)
//{
//	if (pc->sz == 0)
//	{
//		printf("通讯录为空,无法删除\n");
//		return;
//	}
//	char name[MAX_NAME] = {0};
//	assert(pc);
//	//删除
//	printf("请输入要删除的人名字:>");
//	scanf("%s", name);
//
//	//找到要删除的人
//	int i = 0;
//	int del = 0;
//	int flag = 0;
//	for (i = 0; i < pc->sz; i++)
//	{
//		if (strcmp(pc->data[i].name, name) == 0)
//		{
//			del = i;
//			flag = 1; 
//			break;
//		}
//	}
//	if (flag == 0)
//	{
//		printf("要删除的人不存在\n");
//		return;
//	}
//	//删除坐标位del的联系人
//	for (i = del; i < pc->sz-1; i++)
//	{
//		pc->data[i] = pc->data[i + 1];
//	}
//	pc->sz--;
//
//	printf("成功删除联系人\n");
//}

//动态
void DelContact(Contact* pc)
{
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除!\n");
		return;
	}
	char name[MAX_NAME] = { 0 };
	assert(pc);

	//查找
	printf("请输入要删除的人的名字:>");
	scanf("%s", name);
	int del = FindByName(pc, name);
	if (del == -1)
	{
		printf("要删除的人不存在\n");
		return;
	}

	//删除
	int i = 0;
	for (i = del; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;

	printf("成功删除联系人\n");
}

void SearchContact(const Contact* pc)
{
	assert(pc);

	char name[MAX_NAME] = { 0 };
	printf("请输入要查找人的名字:>");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要查找的人不存在\n");
	}
	else
	{
		printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");
		//打印数据
		printf("%-10s\t%-4d\t%-5s\t%-12s\t%-30s\n",
			pc->data[pos].name,
			pc->data[pos].age,
			pc->data[pos].sex,
			pc->data[pos].tele,
			pc->data[pos].addr);
	}
}

void ModifyContact(Contact* pc)
{
	assert(pc);

	char name[MAX_NAME] = { 0 };
	printf("请输入要修改人的名字:>");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1)
		printf("要修改的人不存在\n");
	else
	{
		printf("请输入名字:>");
		scanf("%s", pc->data[pos].name);
		printf("请输入年龄:>");
		scanf("%d", &(pc->data[pos].age));
		printf("请输入性别:>");
		scanf("%s", pc->data[pos].sex);
		printf("请输入电话:>");
		scanf("%s", pc->data[pos].tele);
		printf("请输入地址:>");
		scanf("%s", pc->data[pos].addr);

		printf("修改成功\n");
	}
}

int CmpByName(void* p1, void* p2)
{
	return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}

int CmpByAge(void* p1, void* p2)
{
	return ((PeoInfo*)p1)->age - ((PeoInfo*)p2)->age;
}

void SortContact(Contact* pc) 
{
	int option = 0;
	printf("****1.姓名   2.年龄****\n");
	printf("按着姓名还是年龄排序?>");
	scanf("%d", &option);
	if (option == 1)
	{
		qsort(pc->data, pc->sz, sizeof(PeoInfo), CmpByName); 
	}
	else
	{
		qsort(pc->data, pc->sz, sizeof(PeoInfo), CmpByAge);
	}
}

void DestroyContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
}

void SaveContact(Contact* pc)
{
	FILE* pf = fopen("data.txt", "wb");
	if (!pf)
	{
		perror("fopen");
		return;
	}

	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
	}

	fclose(pf);
	pf = NULL;
}
#define _CRT_SECURE_NO_WARNINGS 1

#include "contact.h"

void menu()
{
	printf("********************************\n");
	printf("***** 1. add     2. del    *****\n");
	printf("***** 3. search  4. modify *****\n");
	printf("***** 5. show    6. sort   *****\n");
	printf("***** 0. exit              *****\n");
	printf("********************************\n");
}

void test()
{
	int input = 0;
	//首先得有通讯录
	Contact con;
	InitContact(&con);

	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);
			break;
		case DEL:
			DelContact(&con);
			break;
		case SEARCH:
			SearchContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SHOW:
			ShowContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case EXIT:
			SaveContact(&con); 
			DestroyContact(&con);
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误, 重新选择\n");
			break;
		}
	} while (input);
}

int main()
{
	test();
	return 0;
}

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

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

相关文章

并发与并行的区别(详细介绍)

并发和并行的区别为&#xff1a;意思不同、侧重不同、处理不同。 一、意思不同 1、并发&#xff1a;并发是指两个或多个事件在同一时间间隔发生&#xff0c;把任务在不同的时间点交给处理器进行处理。在同一时间点&#xff0c;任务并不会同时运行。 2、并行&#xff1a;并行…

【uniapp】更改富文本编辑器图片大小

代码块 //<view v-html"productDetails"></view><rich-text :nodes"productDetails"></rich-text>// 假设htmlContent字段是后台返回的富文本字段var htmlContent res.result.productDetailsconst regex new RegExp(<img, gi…

macOS Big Sur 11.7.9 (20G1426) 正式版 ISO、PKG、DMG、IPSW 下载

macOS Big Sur 11.7.9 (20G1426) 正式版 ISO、PKG、DMG、IPSW 下载 本站下载的 macOS 软件包&#xff0c;既可以拖拽到 Applications&#xff08;应用程序&#xff09;下直接安装&#xff0c;也可以制作启动 U 盘安装&#xff0c;或者在虚拟机中启动安装。另外也支持在 Window…

nginx怎么做负载均衡

Nginx怎么做负载均衡 Nginx 是一个高性能的开源反向代理服务器&#xff0c;可以用于实现负载均衡。负载均衡指的是将用户请求平均分配给多个服务器&#xff0c;以提高整体系统性能和可靠性。下面是一个详细介绍如何使用 Nginx 实现负载均衡的步骤&#xff1a; 步骤 1&#xf…

vue项目打包成App

地址一 地址二 一、将项目开发完成后&#xff0c;在vue.config.js 文件中添加路径 publicPath:‘./’ 在router/index.js关闭路由的history模式&#xff08;默认哈希&#xff09; 二、npm run build&#xff0c;生成的dist文件目录 三、打开 HBuilder X 开发工具 新建 >…

线性代数(基础篇):第一章:行列式 、第二章:矩阵

文章目录 线性代数0&#xff1a;串联各章等价条件 第1章 行列式1.行列式的定义(1)行列式的本质定义(2)行列式的逆序数法定义(3)行列式的展开定理 (第三种定义) 2.行列式的性质3.行列式的公式4.基本行列式(1)主对角线行列式(2)副对角线行列式(3)拉普拉斯行列式(4)范德蒙德行列式…

SpringBoot项目——springboot配置Tomcat两个端口,https和http的方式 jar的打包和运行

目录 引出springboot配置Tomcat两个端口&#xff0c;https和http的方式1.生成SSL证书2.配置client.p12和https端口3.配置http的8080端口WebServerFactoryCustomizer接口4.启动项目 项目应用&#xff1a;在某项目中有一个功能需要https协议Tomcat启动https和http两个端口根据htt…

深度学习中标量,向量,矩阵和张量

1.标量(Scalar) 只有大小没有方向&#xff0c;可用实数表示的一个量 2.向量(Vector) 可以表示大小和方向的量 3.矩阵(Matrix) m行n列,矩阵中的元素可以是数字也可以是符号&#xff0c;在深度学习中一般是二维数组 4.张量(Tensor) 用来表示一些向量、标量和其他张量之间的…

Vue3使用Pinia-store选项式api和组合式api两种使用方式-快速入门demo

Pinia官方文档 选项式api /** Author: Jackie* Date: 2023-06-25 09:58:10* LastEditTime: 2023-07-24 17:32:25* LastEditors: Jackie* Description: pinia* FilePath: /vue3-demo-pinia/src/store/counter.js* version:*/ import { defineStore, storeToRefs } from pinia;…

el-table 表头设置渐变色

<el-table :data"tableData" stripe><el-table-column prop"name" label"测试" align"left"></el-table-column><el-table-column prop"code" label"测试1" align"left"></…

【《Go编程进阶实战:开发命令行应用、HTTP应用和gRPC应用》——指导你使用Go语言构建健壮的、生产级别的应用程序】

谷歌在2009年发布了Go编程语言&#xff0c;并于2012年发布了1.0版。Go语言具有强大的兼容性&#xff0c;一直用于编写可扩展的重量级程序(命令行应用程序、关键基础设施工具乃至大规模分布式系统)。凭借简单性、丰富的标准库和蓬勃发展的第三方软件包生态系统&#xff0c;Go语言…

渗透D1---基础知识回顾

端口&#xff1a; http 80 https 443 ftp 20 21 telnet 23 ssh 22 DNS 53 DHCP 67 68 mail smtp 25 pop3 110 ladp 389 域控制器 3306 mysql 关系型 sqlserver 1433 c# oracle 1521 3389 windows 远程连接端口 redis nosql 6379 编码介绍&#xff1a; 主要有两…

Git实现同一个项目多个版本

需求&#xff1a; 最近项目有这样一个需求&#xff0c;就是同一个项目要求给不同的两个客户&#xff0c;这两个客户需要的功能和界面不一样但基础功能一样&#xff0c;然后修改基础功能时这两个项目的基础功能要同时修改。避免同样的代码在两个项目上各自再开发一遍。 解决办…

前端JS识别二维码内容

原文&#xff1a;https://www.cnblogs.com/houxianzhou/p/15030351.html <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>图片二维码识别</title><script src"https://cdn.bootcss.com/jquery/3.4.1/jque…

如何在3ds max中创建可用于真人场景的巨型机器人:第 5 部分

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. After Effects 中的项目设置 步骤 1 打开“后效”。 打开后效果 步骤 2 我有真人版 我在After Effects中导入的素材。这是将 用作与机器人动画合成的背景素材。 实景镜头 步骤 3 有背景 选定的素材…

halcon微积分原理生成卡尺,异形产品宽度测量

1.普通测量项目中&#xff0c;我们可以利用halcon的测量模型&#xff0c;例如add_metrology_object_line_measure。很方便的测量直线&#xff0c;圆&#xff0c;椭圆&#xff0c;矩形等。这些工具都有一个缺点是&#xff0c;需要提前绘制测量位置&#xff0c;然后利用仿射变换跟…

力扣1116.打印零与奇偶数--------题解

题目描述 现有函数 printNumber 可以用一个整数参数调用&#xff0c;并输出该整数到控制台。 例如&#xff0c;调用 printNumber(7) 将会输出 7 到控制台。 给你类 ZeroEvenOdd 的一个实例&#xff0c;该类中有三个函数&#xff1a;zero、even 和 odd 。ZeroEvenOdd 的相同实…

使用langchain与你自己的数据对话(二):向量存储与嵌入

之前我以前完成了“使用langchain与你自己的数据对话(一)&#xff1a;文档加载与切割”这篇博客&#xff0c;没有阅读的朋友可以先阅读一下&#xff0c;今天我们来继续讲解deepleaning.AI的在线课程“LangChain: Chat with Your Data”的第三门课&#xff1a;向量存储与嵌入。 …

java整合chatGPT

public void test_chatGPT() throws IOException {String pro "127.0.0.1";//本机地址int pro1 7890; //代理端口号//创建一个 HttpHost 实例&#xff0c;这样就设置了代理服务器的主机和端口。HttpHost httpHost new HttpHost(pro, pro1);//创建一个 RequestConf…

Appium+python自动化(二十)- Monkey(猴子)日志(超详解)

简介 日志是非常重要的&#xff0c;用于记录系统、软件操作事件的记录文件或文件集合&#xff0c;可分为事件日志和消息日志。具有处理历史数据、诊断问题的追踪以及理解系统、软件的活动等重要作用&#xff0c;在开发或者测试软系统过程中出现了问题&#xff0c;我们首先想到的…