【C语言】动态通讯录 -- 详解

⚪前言

前面详细介绍了静态版通讯录【C语言】静态通讯录 -- 详解_炫酷的伊莉娜的博客-CSDN博客,但是静态版通讯录的空间是无法被改变的,而且空间利用率也不高。为了解决静态通讯录这一缺点,这时就要有一个能够随着存入联系人数量的增加而增大空间的通讯录。接下来我们将探讨动态通讯录,在能够实现静态版通讯录的基础上,能够灵活调整通讯录联系人数量并实现其基本功能。

以下内容是在静态通讯录的基础上进行修改,大致思路不变。


一、实现目标

1、功能

  • 保存 1000 个联系人的信息
  • 添加联系人
  • 删除联系人
  • 修改联系人
  • 查找联系人
  • 排序

2、个人信息(结构体)

  • 名字
  • 年龄
  • 性别
  • 电话
  • 地址

动态版通讯录的要求:

  1. 默认能够存放 3 个联系人信息。
  2. 不够的话,每次增加 2 个联系人信息。

二、创建文件

  1. test.c(专门测试通讯录的功能)
  2. contact.c(接口的实现)
  3. contact.h(接口的声明)

三、初始页面的实现

1、实现主菜单页面

// test.c

(1)menu()
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");
}

效果图如下:


(2)main()
int main()
{
	int input = 0;
	// 创建通讯录
	struct Contact con; // con就是通讯录,里边包含:data指针和size,capacity
	// 初始化通讯录
	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:
			DestoryContact(&con); // 销毁通讯录-释放动态开辟的内存
			printf("退出通讯录!\n");
			break;
		default:
			printf("选择错误!\n");
			break;
		}
	} while (input);
	return 0;
}

main() 函数代码中与之前的区别主要就是要在初始化的时候开辟动态内存空间,在输入 0 退出程序时,会将通讯录删除。其实仔细想想,确实需要这一步,因为在整个程序都没有结束时,动态开辟内存也并没有结束,所以只能是在整个程序结束的时候才能将动态开辟的内存释放掉。 


2、在contact.h 中引用所需要的文件和必要的定义

首先我们要 声明一个结构体,其中 包含了我们想要保存的信息(可以自己设置):
typedef struct PeoInfo
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
}PeoInfo;

 定义结构体(与静态通讯录不同!)

        动态通讯录在定义结构体时,应该增加一个元素(capacity)来记录当前通讯录的最大容量,当达到这个最大容量的时候,就对动态通讯录进行扩容,增加内存空间,这样就能够很好地实现动态通讯录。 

        静态通讯录与动态通讯录最大的不同就在这,静态版是直接使用一个开辟好的数组,而动态版是使用一个指针变量,用于指向使用动态内存申请函数 malloc 所开辟的空间,然后使用开辟的这块空间对联系人信息进行储存,最大的优点是可以使用 realloc 函数对其进行扩容。这样就解决了静态通讯录在空间上的不足。

// 动态通讯录类型
typedef struct Contact
{
	struct PeoInfo *data; // 存放数据
	int size; // 记录当前已经有的元素个数
	int capacity; // 记录当前通讯录的最大容量
}Contact;

// 静态通讯录类型
struct Contact
{
	struct PeoInfo data[MAX];// 可以存放1000个人的信息
	int size;// 记录通讯录中已经保存的信息个数
};

整体逻辑:

// contact.h
#pragma once // 避免头文件被重复引用

// #define MAX 1000

#define DEFAULT_SZ 3 // 通讯录初始状态的容量大小

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

#include <stdio.h>
#include <string.h> // memset strcmp
#include <stdlib.h> // qsort malloc realloc
#include <assert.h> // assert

enum Option
{
	EXIT,//0
	ADD,//1
	DEL,//2
	SEARCH,//3
	MODIFY,//4
	SHOW,//5
	SORT//6
};

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

// 动态通讯录类型
typedef struct Contact
{
	struct PeoInfo *data; // 存放数据
	int size; // 记录当前已经有的元素个数
	int capacity; // 记录当前通讯录的最大容量
}Contact;

// 声明函数
// 初始化通讯录
void InitContact(struct Contact* ps);
// 增加联系人
void AddContact(struct Contact* ps);
// 展示通讯录信息
void ShowContact(const struct Contact* ps);
// 删除指定联系人信息
void DelContact(struct Contact* ps);
// 查找指定联系人信息
void SearchContact(const struct Contact* ps);
// 修改指定联系人信息
void ModifyContact(struct Contact* ps);
// 排序通讯录内容(按姓名排)
void SortContact(struct Contact* ps);
// 销毁通讯录
void DestoryContact(Contact* ps);

四、在 contact.c 上实现各个接口函数

1、初始化通讯录

malloc 函数在这里并没有把其数据初始化为 0 ,这里可以使用 memset 函数初始化存放联系人信息的结构体数组 data。

对于动态通讯录的初始化,就是申请一块初始的空间,用来存放信息,如果满了在在此基础上进行扩容。

// 初始化通讯录
void InitContact(struct Contact* ps)
{
	assert(ps);
	ps->size = 0;
	ps->capacity = DEFAULT_SZ;
	ps->data = (struct PeoInfo*)malloc(ps->capacity * sizeof(struct PeoInfo));
	if (ps->data == NULL)
	{
		perror("InitContact::malloc");
		return;
	}
	memset(ps->data, 0, ps->capacity * sizeof(struct PeoInfo));
}

2、添加联系人

先判断通讯录人数是否已满,满了需要扩容,对已经申请过的空间进行扩容,可以使用库函数 realloc 实现。满了一次扩容 2 联系人信息的空间。

// 检测当前通讯录的容量
void CheckCapacity(struct Contact* ps)
{
	if (ps->size == ps->capacity)
	{
		//增容
		struct PeoInfo* ptr = realloc(ps->data, (ps->capacity + 2) * sizeof(PeoInfo));
		if (ptr != NULL)
		{
			ps->data = ptr;
			ps->capacity += 2;
			printf("增容成功!\n");
		}
		else
		{
			printf("增容失败!\n");
		}
	}
}

// 添加联系人
void AddContact(struct Contact* ps)
{
	assert(ps);

	// 检测当前通讯录的容量
	// 1、如果满了就增加空间
	// 2、如果没满无需任何操作
	CheckCapacity(ps);
	// 增加数据
	printf("请输入名字:>");
	scanf("%s", ps->data[ps->size].name);
	printf("请输入年龄:>");
	scanf("%d", &(ps->data[ps->size].age)); // 年龄是一个整型变量 需要取地址
	printf("请输入性别:>");
	scanf("%s", ps->data[ps->size].sex);
	printf("请输入电话:>");
	scanf("%s", ps->data[ps->size].tele);
	printf("请输入地址:>");
	scanf("%s", ps->data[ps->size].addr);

	ps->size++;
	printf("添加成功!\n");
}

 注意由于 age 在这里是一个整型变量,所以要加上 &


3、展示通讯录中的信息

// 展示通讯录中的信息
void ShowContact(const struct Contact* ps)
{
	if (ps->size == 0)
	{
		printf("通讯录为空!\n"); // 判断通讯录人数是否为0

	}
	else
	{
		int i = 0;
		// 打印标题
		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
		// 打印数据
		for (i = 0; i < ps->size; i++)
		{
			printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
				ps->data[i].name,
				ps->data[i].age,
				ps->data[i].sex,
				ps->data[i].tele,
				ps->data[i].addr);
		}
	}
}

注意:因为只是打印信息,不会修改内容,所以加上 const 更加安全。 

为了让结果更美观,采用了左对齐的方式。printf 中 %-20s 中的 20 是指输出字段的宽度负号表示左对齐,如省略表示右对齐,如果输出的数据位数小于 20,则在数据右端补齐空格。


4、通过姓名查找指定联系人所在的下标 

// 通过姓名查找指定联系人所在下标
static int FindByName(const struct Contact* ps, char name[MAX_NAME])
{
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		if (0 == strcmp(ps->data[i].name, name))
		{
			return i; // 查找到返回联系人的下标
		}
	}

	return -1; // 找不到返回-1
}

为了解决代码的冗余,我们不妨分装一个函数来完成查找功能。该函数会在删除 / 修改 / 查找指定联系人信息的函数中被调用到,这些函数刚好又在同一源文件中,所以用 static 修饰函数,只能在所在此文件内使用。


5、删除指定联系人信息

先判断通讯录是否为空,如果不为空,再通过 FindByName() 函数查找通讯录中是否有你要删除的联系人,如果有则删除。

原理:把要删除的联系人后面一个人的信息依次从前向后往前移动一位,覆盖掉其信息,然后再将人数 -1。

// 删除指定联系人
void DelContact(struct Contact* ps)
{
	char name[MAX_NAME];
	int pos = 0;
	printf("请输入要删除人的名字:>");
	scanf("%s", name);
	// 1、查找要删除的人在什么位置
	// 找到了返回名字所在元素的下标
	// 找不到返回 -1
	pos = FindByName(ps, name);

	// 2、删除
	if (pos == -1)
	{
		printf("要删除的人不存在!\n");
	}
	else
	{
		// 删除数据
		int j = 0;
		for (j = pos; j < ps->size - 1; j++)
		{
            // 把要删除的人后面一个人的信息依次从前向后往前移动一位,覆盖掉其信息
			ps->data[j] = ps->data[j + 1];
		}
		ps->size--;
		printf("删除成功!\n");
	}
}

6、查找指定联系人信息

通过 FindByName() 函数查找通讯录中是否有你要查找的联系人,如果查找到,则打印该联系人的信息。 

// 查找指定联系人信息
void SearchContact(const struct Contact* ps)
{
	int pos = 0;
	char name[MAX_NAME];
	printf("请输入要查找人的名字:>");
	scanf("%s", name);
	pos = FindByName(ps, name); // 查找
	if (pos == -1)
	{
		printf("要查找的人不存在!\n");
	}
	else
	{
		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
		//数据
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
			ps->data[pos].name,
			ps->data[pos].age,
			ps->data[pos].sex,
			ps->data[pos].tele,
			ps->data[pos].addr);
	}
}

 注意:因为只是查找信息,不会修改内容,所以加上 const 更加安全 。


7、修改指定联系人信息

通过 FindByName() 函数查找通讯录中是否有你要修改信息的联系人,如果有则修改。 

// 修改指定联系人信息
void ModifyContact(struct Contact* ps)
{
	int pos = 0;
	char name[MAX_NAME];
	printf("请输入要修改人的名字:>");
	scanf("%s", name);
	pos = FindByName(ps, name);
	if (pos == -1)
	{
		printf("要修改人的信息不存在!\n");
	}
	else
	{
		printf("请输入名字:>");
		scanf("%s", ps->data[pos].name);
		printf("请输入年龄:>");
		scanf("%d", &ps->data[pos].age);
		printf("请输入性别:>");
		scanf("%s", ps->data[pos].sex);
		printf("请输入电话:>");
		scanf("%s", ps->data[pos].tele);
		printf("请输入地址:>");
		scanf("%s", ps->data[pos].addr);

		printf("修改完成!\n");
	}
}

8、排序通讯录内容

// 排序通讯录内容
// 比较结构体数组中两个元素的姓名成员
int compareStructType_name(const void* elem1, const void* elem2)
{
	// 姓名成员是字符串,用strcmp比较
	return strcmp(((struct PeoInfo*)elem1)->name, ((struct PeoInfo*)elem2)->name);
}

// 以名字排序所有联系人
void SortContact(struct Contact* ps)
{
	// 判断通讯录是否为空
	if (ps->size == 0)
	{
		printf("通讯录为空,无法排序!\n");
		return;
	}
	else
	{
		// 根据姓名成员对结构体数组升序排列
		qsort(ps->data, ps->size, sizeof(ps->data[0]), compareStructType_name);
		printf("以名字排序联系人成功!\n");
	}
}

该函数排序用的是姓名作为标准,也可以自己更换其他的排序方式。


9、销毁通讯录

使用动态内存开辟的空间是需要归还的,当通讯录使用完后是需要归还内存的,也就需要我们销毁通讯录。

// 销毁通讯录
void DestoryContact(Contact* ps)
{
	free(ps->data);
	ps->data = NULL;
	ps->capacity = 0;
	ps->size = 0;
	printf("销毁成功!\n");
}

五、代码整合

1、test.c

// test.c
#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");
}

int main()
{
	int input = 0;
	// 创建通讯录
	struct Contact con; // con就是通讯录,里边包含:data指针和size,capacity
	// 初始化通讯录
	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:
			DestoryContact(&con); // 销毁通讯录-释放动态开辟的内存
			printf("退出通讯录!\n");
			break;
		default:
			printf("选择错误!\n");
			break;
		}
	} while (input);
	return 0;
}

2、contact.h

// contact.h
#pragma once // 避免头文件被重复引用

// #define MAX 1000

#define DEFAULT_SZ 3 // 通讯录初始状态的容量大小

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

#include <stdio.h>
#include <string.h> // memset strcmp
#include <stdlib.h> // qsort malloc realloc
#include <assert.h> // assert

enum Option
{
	EXIT,//0
	ADD,//1
	DEL,//2
	SEARCH,//3
	MODIFY,//4
	SHOW,//5
	SORT//6
};

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

// 动态通讯录类型
typedef struct Contact
{
	struct PeoInfo *data; // 存放数据
	int size; // 记录当前已经有的元素个数
	int capacity; // 记录当前通讯录的最大容量
}Contact;

// 声明函数
// 初始化通讯录
void InitContact(struct Contact* ps);
// 增加联系人
void AddContact(struct Contact* ps);
// 展示通讯录信息
void ShowContact(const struct Contact* ps);
// 删除指定联系人信息
void DelContact(struct Contact* ps);
// 查找指定联系人信息
void SearchContact(const struct Contact* ps);
// 修改指定联系人信息
void ModifyContact(struct Contact* ps);
// 排序通讯录内容(按姓名排)
void SortContact(struct Contact* ps);
// 销毁通讯录
void DestoryContact(Contact* ps);

3、contact.c

// contact.c
#define _CRT_SECURE_NO_WARNINGS 1

#include "contact.h"

// 初始化通讯录
void InitContact(struct Contact* ps)
{
	assert(ps);
	ps->size = 0;
	ps->capacity = DEFAULT_SZ;
	ps->data = (struct PeoInfo*)malloc(ps->capacity * sizeof(struct PeoInfo));
	if (ps->data == NULL)
	{
		perror("InitContact::malloc");
		return;
	}
	memset(ps->data, 0, ps->capacity * sizeof(struct PeoInfo));
}

// 检测当前通讯录的容量
void CheckCapacity(struct Contact* ps)
{
	if (ps->size == ps->capacity)
	{
		//增容
		struct PeoInfo* ptr = realloc(ps->data, (ps->capacity + 2) * sizeof(PeoInfo));
		if (ptr != NULL)
		{
			ps->data = ptr;
			ps->capacity += 2;
			printf("增容成功!\n");
		}
		else
		{
			printf("增容失败!\n");
		}
	}
}

// 添加联系人
void AddContact(struct Contact* ps)
{
	assert(ps);

	// 检测当前通讯录的容量
	// 1、如果满了就增加空间
	// 2、如果没满无需任何操作
	CheckCapacity(ps);
	// 增加数据
	printf("请输入名字:>");
	scanf("%s", ps->data[ps->size].name);
	printf("请输入年龄:>");
	scanf("%d", &(ps->data[ps->size].age)); // 年龄是一个整型变量 需要取地址
	printf("请输入性别:>");
	scanf("%s", ps->data[ps->size].sex);
	printf("请输入电话:>");
	scanf("%s", ps->data[ps->size].tele);
	printf("请输入地址:>");
	scanf("%s", ps->data[ps->size].addr);

	ps->size++;
	printf("添加成功!\n");
}

// 展示通讯录中的信息
void ShowContact(const struct Contact* ps)
{
	if (ps->size == 0)
	{
		printf("通讯录为空!\n"); // 判断通讯录人数是否为0

	}
	else
	{
		int i = 0;
		// 打印标题
		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
		// 打印数据
		for (i = 0; i < ps->size; i++)
		{
			printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
				ps->data[i].name,
				ps->data[i].age,
				ps->data[i].sex,
				ps->data[i].tele,
				ps->data[i].addr);
		}
	}
}

// 通过名字查找指定联系人所在的下标
static int FindByName(const struct Contact* ps, char name[MAX_NAME])
{
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		if (0 == strcmp(ps->data[i].name, name))
		{
			return i;
		}
	}

	return -1; // 找不到返回-1
}

// 删除指定联系人
void DelContact(struct Contact* ps)
{
	char name[MAX_NAME];
	int pos = 0;
	printf("请输入要删除人的名字:>");
	scanf("%s", name);
	// 1、查找要删除的人在什么位置
	// 找到了返回名字所在元素的下标
	// 找不到返回 -1
	pos = FindByName(ps, name);

	// 2、删除
	if (pos == -1)
	{
		printf("要删除的人不存在!\n");
	}
	else
	{
		// 删除数据
		int j = 0;
		for (j = pos; j < ps->size - 1; j++)
		{
            // 把要删除的人后面一个人的信息依次从前向后往前移动一位,覆盖掉其信息
			ps->data[j] = ps->data[j + 1];
		}
		ps->size--;
		printf("删除成功!\n");
	}
}

// 查找指定联系人信息
void SearchContact(const struct Contact* ps)
{
	int pos = 0;
	char name[MAX_NAME];
	printf("请输入要查找人的名字:>");
	scanf("%s", name);
	pos = FindByName(ps, name); // 查找
	if (pos == -1)
	{
		printf("要查找的人不存在!\n");
	}
	else
	{
		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
		//数据
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
			ps->data[pos].name,
			ps->data[pos].age,
			ps->data[pos].sex,
			ps->data[pos].tele,
			ps->data[pos].addr);
	}
}

// 修改指定联系人信息
void ModifyContact(struct Contact* ps)
{
	int pos = 0;
	char name[MAX_NAME];
	printf("请输入要修改人的名字:>");
	scanf("%s", name);
	pos = FindByName(ps, name);
	if (pos == -1)
	{
		printf("要修改人的信息不存在!\n");
	}
	else
	{
		printf("请输入名字:>");
		scanf("%s", ps->data[pos].name);
		printf("请输入年龄:>");
		scanf("%d", &ps->data[pos].age);
		printf("请输入性别:>");
		scanf("%s", ps->data[pos].sex);
		printf("请输入电话:>");
		scanf("%s", ps->data[pos].tele);
		printf("请输入地址:>");
		scanf("%s", ps->data[pos].addr);

		printf("修改完成!\n");
	}
}

// 排序通讯录内容
// 比较结构体数组中两个元素的姓名成员
int compareStructType_name(const void* elem1, const void* elem2)
{
	// 姓名成员是字符串,用strcmp比较
	return strcmp(((struct PeoInfo*)elem1)->name, ((struct PeoInfo*)elem2)->name);
}

// 以名字排序所有联系人
void SortContact(struct Contact* ps)
{
	// 判断通讯录是否为空
	if (ps->size == 0)
	{
		printf("通讯录为空,无法排序!\n");
		return;
	}
	else
	{
		// 根据姓名成员对结构体数组升序排列
		qsort(ps->data, ps->size, sizeof(ps->data[0]), compareStructType_name);
		printf("以名字排序联系人成功!\n");
	}
}

// 销毁通讯录
void DestoryContact(Contact* ps)
{
	free(ps->data);
	ps->data = NULL;
	ps->capacity = 0;
	ps->size = 0;
	printf("销毁成功!\n");
}

六、程序运行效果 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

案例15 Spring Boot入门案例

1. 选择Spring Initializr快速构建项目 ​ 2. 设置项目信息 ​ 3. 选择依赖 ​ 4. 设置项目名称 ​ 5. 项目结构 ​ 6. 项目依赖 自动配置了Spring MVC、内置了Tomcat、配置了Logback(日志)、配置了JSON。 ​ 7. 创建HelloController类 com.wfit.boot.hello目录下创建HelloCo…

【云原生】Docker 详解(二):Docker 架构及工作原理

Docker 详解&#xff08;二&#xff09;&#xff1a;Docker 架构及工作原理 Docker 在运行时分为 Docker 引擎&#xff08;服务端守护进程&#xff09; 和 客户端工具&#xff0c;我们日常使用各种 docker 命令&#xff0c;其实就是在使用 客户端工具 与 Docker 引擎 进行交互。…

哈夫曼树(赫夫曼树、最优树)详解

目录 哈夫曼树&#xff08;赫夫曼树、最优树&#xff09;详解 哈夫曼树相关的几个名词 什么是哈夫曼树 构建哈夫曼树的过程 哈弗曼树中结点结构 构建哈弗曼树的算法实现 哈夫曼树&#xff08;赫夫曼树、最优树&#xff09;详解 哈夫曼树相关的几个名词 路径&#xff1a;…

深入理解 Flutter 图片加载原理

作者&#xff1a;京东零售 徐宏伟 来源&#xff1a;京东云开发者社区 前言 随着Flutter稳定版本逐步迭代更新&#xff0c;京东APP内部的Flutter业务也日益增多&#xff0c;Flutter开发为我们提供了高效的开发环境、优秀的跨平台适配、丰富的功能组件及动画、接近原生的交互体验…

Nginx详解

1、高并发时代 单台tomcat在理想情况下可支持的最大并发数量在200~500之间&#xff0c;如果大于这个数量可能会造成响应缓慢甚至宕机。 解决方案是通过多台服务器分摊并发压力&#xff0c;这不仅需要有多台tomcat服务器&#xff0c;还需要一台服务器专门用来分配请求。这既是…

Socks5代理在多线程爬虫中的应用

在进行爬虫开发过程中&#xff0c;我们常常需要处理大量的数据&#xff0c;并执行多任务并发操作。然而&#xff0c;频繁的请求可能会引起目标网站的反爬机制&#xff0c;导致IP封禁或限制访问。为了规避这些限制&#xff0c;我们可以借助Socks5代理的强大功能&#xff0c;通过…

产品经理如何突破职业瓶颈,杀出重围?

随着社会的进步和科技的发展&#xff0c;互联网行业从未停止过发展的脚步。而在这个充满机遇和挑战的赛道上&#xff0c;互联网产品经理的角色显得尤为重要。然而&#xff0c;随着互联网产品经理的数量逐年增加&#xff0c;内卷化现象也日益严重。那么&#xff0c;产品经理应该…

一篇文章教会你搭建私人kindle图书馆,并内网穿透实现公网访问

搭建私人kindle图书馆&#xff0c;并内网穿透实现公网访问 在电子书风靡的时期&#xff0c;大部分人都购买了一本电子书&#xff0c;虽然这本电子书更多的时候是被搁置在储物架上吃灰&#xff0c;或者成为盖泡面的神器&#xff0c;但当亚马逊发布消息将放弃电子书在中国的服务…

excel填数据转json格式

定制化比较严重&#xff0c;按需更改 excel文件如下 代码 # -*- coding: utf-8 -*- import oss2 import shutil import sys import xlwt import xlrd import json from datetime import datetime, timedeltafile1 "C:\\Users\\cxy\\Desktop\\generate.xls" #打开表…

操作系统搭建相关知识

文章目录 系统篇netstat命令systemctl命令Systemd系统资源分类&#xff08;12类&#xff09; 网络篇ifconfig命令操作系统配置动态IP脚本dhcp服务的安装与配置防火墙相关知识 操作系统常用配置文件 系统篇 netstat命令 netstat指路 systemctl命令 常用于重启系统的每个服务…

机器学习算法之-逻辑回归(2)

为什么需要逻辑回归 拟合效果太好 特征与标签之间的线性关系极强的数据&#xff0c;比如金融领域中的 信用卡欺诈&#xff0c;评分卡制作&#xff0c;电商中的营销预测等等相关的数据&#xff0c;都是逻辑回归的强项。虽然现在有了梯度提升树GDBT&#xff0c;比逻辑回归效果更…

TCP/IP协议追层分析物理层(第三十九课)

TCP/IP协议追层分析物理层(第三十九课) 1 物理层:建立、维护、断开物理连接,定义了接口及介质,实现了比特流的传输。 1、传输介质分类 有线介质:网线(双绞线)、光纤 无线介质:无线电 微波 激光 红外线 2、双绞线分类: 五类cat5: 适用于100Mbps 超五类cat5e:适用于…

多维时序 | MATLAB实现CNN-BiGRU-Attention多变量时间序列预测

多维时序 | MATLAB实现CNN-BiGRU-Attention多变量时间序列预测 目录 多维时序 | MATLAB实现CNN-BiGRU-Attention多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 MATLAB实现CNN-BiGRU-Attention多变量时间序列预测&#xff0c;CNN-BiGRU-Attent…

Nginx的安装及负载均衡搭建

一.Nginx的安装 1&#xff09;准备安装环境 yum install -y make gcc gcc-c pcre-devel pcre zlib zlib-devel openssl openssl-develPERE PCRE(Perl Compatible Regular Expressions)是一个Perl库&#xff0c;包括 perl 兼容的正则表达式库。 nginx的http模块使用pcre来解…

常见期权策略类型有哪些?

这几天在做一个期权策略类型的整理分类&#xff0c;怎么解释期权策略&#xff0c;期权策略是现代金融市场中运用非常广泛、变化非常丰富、结构非常精妙的金融衍生产品&#xff1b;同时也是一种更为复杂也更为灵活的投资工具&#xff0c;下文介绍常见期权策略类型有哪些&#xf…

使用mysql、java开发的平台软件一键安装

前言 一般web项目会使用mysql数据库、java开发应用程序打包成jar包。 有些项目会需要导入初始化的行政区域信息。 流程图 说明 1. 脚本中提供变量去配置当前项目的区域 2. 安装包里需要包含全国所有的区域信息 3. 运行程序的时候就可以根据配置 &#xff0c;调用接口&am…

在本地搭建WAMP服务器并通过端口实现局域网访问(无需公网IP)

文章目录 前言1.Wamp服务器搭建1.1 Wamp下载和安装1.2 Wamp网页测试 2. Cpolar内网穿透的安装和注册2.1 本地网页发布2.2 Cpolar云端设置2.3 Cpolar本地设置 3. 公网访问测试4. 结语 前言 软件技术的发展日新月异&#xff0c;各种能方便我们生活、工作和娱乐的新软件层出不穷&a…

一百六十、Kettle——Linux上安装的Kettle9.2.0连接Hive3.1.2

一、目标 Kettle9.2.0在Linux上安装好后&#xff0c;需要与Hive3.1.2数据库建立连接 之前已经在本地上用kettle9.2.0连上Hive3.1.2 二、各工具版本 &#xff08;一&#xff09;kettle9.2.0 kettle9.2.0安装包网盘链接 链接&#xff1a;https://pan.baidu.com/s/15Zq9w…

python爬虫数据解析xpath、jsonpath,bs4

数据的解析 解析数据的方式大概有三种 xpathJsonPathBeautifulSoup xpath 安装xpath插件 打开谷歌浏览器扩展程序&#xff0c;打开开发者模式&#xff0c;拖入插件&#xff0c;重启浏览器&#xff0c;ctrlshiftx&#xff0c;打开插件页面 安装lxml库 安装在python环境中的Scri…

NeuralNLP-NeuralClassifier的使用记录(一),训练预测自己的【英文文本多分类】

NeuralNLP-NeuralClassifier的使用记录&#xff0c;训练预测自己的英文文本多分类 NeuralNLP-NeuralClassifier是腾讯开发的一个多层多分类应用工具&#xff0c;支持的任务包括&#xff0c;文本分类中的二分类、多分类、多标签&#xff0c;以及层次多标签分类。支持的文本编码…