通讯录的实现(超详细)——C语言(进阶)
目录
一、创建联系人信息(结构体)
二、创建通讯录(结构体)
三、define定义常量
四、打印通讯录菜单
五、枚举菜单选项
六、初始化通讯录
七、实现通讯的的功能
7.1 增加加联系人
7.2 显示所有联系人的信息
7.3 单独查找人的函数
7.4 删除指定联系人
7.5 查找指定联系人
7.6 修改指定联系人
7.7 按照名字顺序进行排序
7.8 清空所有联系人
八、通讯录优化——静态->动态
8.1 通讯录结构体优化
8.2 初始化函数的优化
8.3增加联系人的优化
8.4 释放通讯录的空间
九、完整代码
test.c
contact.h
contact.c
实现一个通讯录,这里我们先来简单构思一下,通讯录中保存人的信息,分别有姓名,年龄,性别,电话,住址,假设我们通讯录中可以存放1000个人的信息。同时我们通讯录还要具备一些功能。包括;
① 增加联系人
② 删除指定联系人
③ 修改指定联系人
④ 查找指定联系人
⑤ 显示所有联系人的信息
⑥ 按照名字顺序进行排序
⑦ 清空所有联系人
在写完之后,我们紧接着可以对通讯录进行优化,将静态通讯录改为动态通讯录。
一起跟着博主的思路来完成通讯录的实现吧!
首先我们需要三个文件,分别是test.c——测试通讯录,contact.h——函数和类型的声明,contact.c——函数的实现,其次我们需要创建保存人信息的结构体。接下来我们开始慢代码实现吧!
一、创建联系人信息(结构体)
为了保存联系人的各个信息,我们创建了一个结构体,放在头文件中,同时为了方便使用,对结构体重命名。
typedef struct PeoInfo
{char name[20];int age;char sex[5];char tele[12];char addr[30];
}PeoInfo;
二、创建通讯录(结构体)
为了方便记录通讯录中现在的人数,创建一个结构体将联系人结构体和人数封装在一起。
//通讯录
typedef struct Contact
{PeoInfo data[100];int sz;
}Contact;
三、define定义常量
写到这里,我们发现如果以后想要更改通讯录的人数,或者名字的宽度,以及地址的宽度这些变量,要到代码中修改,很麻烦,我们可以通过define定义常量,这样使用或者更改的时候就会很方便,达到我们想要的效果。
#define MAX 100 //最大人数
#define MAX_NAME 20 //名字最大宽度
#define MAX_SEX 5 //性别种类
#define MAX_TELE 12 //电话最大长度
#define MAX_ADDR 30 //地址最大宽度
四、打印通讯录菜单
因为每次使用至少使用一次,所以我们采用do—while循环来设计测试通讯录。
void menu()
{printf("*******************************\n");printf("********1.add 2.del*******\n");printf("********3.search 4.modify****\n");printf("********5.show 6.sort******\n");printf("********* 7.clear***********\n");printf("********* 0.exit ***********\n");printf("*******************************\n");
}
void test()
{int input = 0;do{menu();printf("请选择:> ");scanf("%d", &input);} while(input);
}
五、枚举菜单选项
为了代码美观,以及更方便的去识别代码的意思,我们可以采用自定义枚举的方式来实现功能选择。具体代码如下
enum Option
{Exit,//0 ——退出Add,//1 ——增加联系人Del,//2 ——删除指定联系人Search,//3 ——查找指定联系人信息Modify,//4 ——修改指定联系人信息Show,//5 ——显示通讯录中所有联系人Sort,//6 ——排序联系人Clear//7 ——清空所有联系人};
这样就容易理解许多,效果如下所示:
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 Clear:ClearContact(&con);break;case Exit:printf("退出通讯录\n");break;default:printf("选择错误,请重新选择\n");break;}} while (input);
}
六、初始化通讯录
当我们创建完通讯录之后,为了更好的实现后面的功能,最好初始化一下通讯录。
注意:在contact.h中声明,在contact.c中实现。
这里博主使用memset来讲通讯录中的信息全部初始化为0,如果不想使用memset,也可以采用循环的形式来初始化。具体代码如下:
//初始化通讯录
void InitContact(Contact* pc)
{memset(pc->data, 0, sizeof(pc->data));pc->sz = 0;
}
七、实现通讯的的功能
注意:①以下函数的实行都是在contact.h中声明类型,然后在contact.v中实现
②在进入函数时,需要assert进行断言,防止传入空指针
③如果要进行的操作不需要更改通讯录的内容(例如:显示所有联系人)时,要用const修饰以下,保护通讯录内容。
7.1 增加加联系人
增加联系人我们首先需要判断通讯录里面的人数是否满了。没满才能继续进行操作
//增加联系人
void AddContact(Contact* pc)
{assert(pc);//断言,防止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("成功添加联系人");}
7.2 显示所有联系人的信息
当我们增加完联系人之后,我们想要看看是否成功添加,来看看通讯录中存在的联系人。
注意:我们打印的时候可以借助\t制表符来进行打印,这样打印出来会很整齐,很好看!
//显示所有联系人
void ShowContact(const Contact* pc)
{//断言assert(pc);//打印列标题printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n","姓名","年龄","性别","电话","地址");int i = 0;//打印数据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);}
}
具体效果如下图所示:
7.3 单独查找人的函数
写到这里我们发现不管是删除联系人还是查找联系人以及修改联系人,都会经过查找这一步,所以单独写个查找的函数代码如下:
//只在本文件使用
static int FindName(const Contact* pc, char name[])
{int i = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name)==0){return i;}}return -1;
}
7.4 删除指定联系人
当我们写完单独查找人的函数后,这个删除指定联系人就会简单许多,在查找前仍然要检查一下通讯录是否为空,如果为空就没必要再继续下去了,先查找到指定联系人,看看是否存在,然后再删除,具体代码如下:
//删除指定联系人
void DelContact(Contact* pc)
{if (pc->sz == 0){printf("通讯录为空,无法删除\n");return;}char name[MAX_NAME]={0};assert(pc);//删除printf("请输入要删除人的名字:");scanf("%s", name);// 不管是删除联系人还是查找联系人以及修改联系人,都会经过查找这一步,所以单独写个查找的函数int del=FindName(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");
}
7.5 查找指定联系人
直接调用单独查找人的函数即可,是不是很方便!如果查找的人不存在,那就直接跳出即可,如果找到,就答应查找的联系人,代码如下:
//查找指定联系人
void SearchContact(const Contact* pc)
{char name[MAX_NAME] = { 0 };assert(pc);printf("请输入要查找人的名字:");scanf("%s", name);int pos = FindName(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);}
}
7.6 修改指定联系人
修改联系人很简单,这里就不多做解释啦,直接上代码!
//修改联系人
void ModifyContact(Contact* pc)
{char name[MAX_NAME] = { 0 };assert(pc);printf("请输入要修改联系人的姓名:");scanf("%s", name);int pos = FindName(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);}
}
7.7 按照名字顺序进行排序
这里博主就拿按照名字顺序进行排序,利用库函数qsort函数进行排序,具体qsort函数是什么,博主这里建议大家去查一查,了解一下,当然小编也准备单独写一篇博客来介绍qsort函数以及qsort的模拟实现,如果感兴趣,可以关注,期待小编更新哦。
int cmp_name(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}
//排序联系人
void SortContact(Contact* pc)
{assert(pc);//就拿按照名字排序吧//利用qsort函数qsort(pc->data, pc->sz, sizeof((pc->data)[0]), cmp_name);//排序结束,输出通讯录//打印列标题printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");int i = 0;//打印数据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);}
}
7.8 清空所有联系人
清空联系人也会很简单,就直接讲通讯录里面的sz人数置为0即可,具体代码如下
void ClearContact(Contact* pc)
{assert(pc);//其实清空联系人只需要把里面的人数清零即可int i = 0;printf("确定要清空所有联系人吗?确定请按1,按其余键返回\n");scanf("%d", &i);if (i == 1){pc->sz = 0;printf("清空联系人成功");}
}
八、通讯录优化——静态->动态
写到这里,其实简单的通讯录已经介绍完毕,但是我们这个通讯录其实可以进行优化的。
优化目标:
①通讯录空间不是固定的,大小可以调整的。
②默认能放三个人的信息,如果不够,就每次增加两个人的容量
8.1 通讯录结构体优化
因为这里的通讯录人数不是固定的了,所以我们首先想到的是优化通讯录这个结构。通讯录的空间是动态开辟的,由malloc开辟即可。
注意:①sz是记录当前放的有效元素的个数
②capacity是记录通讯录当前最大容量
#define DEFAULT_SZ 3 //起始容量
#define ADD_SZ 2 //每次需要增加的容量//动态版本//通讯录容量可以更改,不够时进行增加
typedef struct contact
{PeoInfo* data;//指向存放数据的空间int sz;//当前放的有效元素的个数int capacity;//通讯录当前最大容量
}Contact;
8.2 初始化函数的优化
初始化函数也不能是固定的100人全部初始化。而是应该最开始开辟三个人的空间。人数为0,容量为3。
//动态版本
void InitContact(Contact* pc)
{assert(pc);pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));if (pc->data == NULL){perror("InitContact");return;}pc->sz = 0;pc->capacity = DEFAULT_SZ;
}
8.3增加联系人的优化
这时动态版本的增加联系人就不存在满的问题了,而是先检查人数是否等于这里的最大容量的,如果等于,那就需要进行扩容了。(利用realloc函数)这里的好处就体现出来了,我们用多少空间,就开辟多少空间,每次只多开辟两个人的空间即可。
我们这里用int来接收增容的结果,如果增容成功,就返回1,增容失败就返回0,然后在进行判断,如果失败,就没必要继续往下走了。
//动态版本
void AddContact(Contact* pc)
{assert(pc);//断言,防止pc是空指针CheckCapacity(pc);if (0 == CheckCapacity(pc)){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");}
int CheckCapacity(Contact* pc)
{assert(pc);//判断通讯录是否满了if (pc->sz == pc->capacity){PeoInfo* ptr=(PeoInfo*)realloc(pc->data, (pc->capacity+ADD_SZ)*sizeof(PeoInfo));if (ptr == NULL){perror("CheckCapacity");return 0;}else{pc->data = ptr;pc->capacity += ADD_SZ;printf("增容成功\n");return 1;}}return 1;
}
8.4 释放通讯录的空间
因为这里的通讯录是动态开辟,所以当我们使用完之后要将开辟的内存释放掉,防止内存泄漏。
当然释放完空间,我们里面的信息也就没有了,这时要把人数置为0,容量变为3.
//因为通讯录是动态开辟的,所以使用完需要释放
void DestroyContact(Contact* pc)
{free(pc->data);pc->data = NULL;pc->capacity = 0;pc->sz = 0;
}
九、完整代码
到这里,我们不管是简单的静态通讯录,还是动态的通讯录都实现完毕,代码博主这里放在一起来展示。
test.c
#define _CRT_SECURE_NO_WARNINGS 1#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("********** 7.clear***********\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 Clear:ClearContact(&con);break;case Exit:DestroyContact(&con);printf("退出通讯录\n");break;default:printf("选择错误,请重新选择\n");break;}} while (input);
}
int main()
{test();return 0;
}
contact.h
#define _CRT_SECURE_NO_WARNINGS 1#pragma once#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30#define DEFAULT_SZ 3
#define ADD_SZ 2#include<string.h>#include<stdio.h>#include<assert.h>#include<stdlib.h>//类型的声明enum Option
{Exit,//0Add,//1Del,//2Search,//3Modify,//4Show,//5Sort,//6Clear//7};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 ClearContact(Contact* pc);//释放通讯录的空间
void DestroyContact(Contact* pc);
contact.c
#define _CRT_SECURE_NO_WARNINGS 1#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
//函数的实现//静态版本
初始化通讯录
//void InitContact(Contact* 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 == NULL){perror("InitContact");return;}pc->sz = 0;pc->capacity = DEFAULT_SZ;
}//静态版本
//增加联系人
//void AddContact(Contact* pc)
//{
// assert(pc);//断言,防止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("成功添加联系人");
//
//}//动态版本
int CheckCapacity(Contact* pc)
{assert(pc);//判断通讯录是否满了if (pc->sz == pc->capacity){PeoInfo* ptr=(PeoInfo*)realloc(pc->data, (pc->capacity+ADD_SZ)*sizeof(PeoInfo));if (ptr == NULL){perror("CheckCapacity");return 0;}else{pc->data = ptr;pc->capacity += ADD_SZ;printf("增容成功\n");return 1;}}return 1;
}
void AddContact(Contact* pc)
{assert(pc);//断言,防止pc是空指针CheckCapacity(pc);if (0 == CheckCapacity(pc)){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);//打印列标题printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");int i = 0;//打印数据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 FindName(const Contact* pc, char name[])
{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;//}// 不管是删除联系人还是查找联系人以及修改联系人,都会经过查找这一步,所以单独写个查找的函数int del = FindName(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)
{char name[MAX_NAME] = { 0 };assert(pc);printf("请输入要查找人的名字:");scanf("%s", name);int pos = FindName(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)
{char name[MAX_NAME] = { 0 };assert(pc);printf("请输入要修改联系人的姓名:");scanf("%s", name);int pos = FindName(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);}
}int cmp_name(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}
//排序联系人
void SortContact(Contact* pc)
{assert(pc);//就拿按照名字排序吧//利用qsort函数qsort(pc->data, pc->sz, sizeof((pc->data)[0]), cmp_name);//排序结束,输出通讯录//打印列标题printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");int i = 0;//打印数据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);}
}//清空联系人void ClearContact(Contact* pc)
{assert(pc);//其实清空联系人只需要把里面的人数清零即可int i = 0;printf("确定要清空所有联系人吗?确定请按1,按其余键返回\n");scanf("%d", &i);if (i == 1){pc->sz = 0;printf("清空联系人成功");}
}//因为通讯录是动态开辟的,所以使用完需要释放
void DestroyContact(Contact* pc)
{free(pc->data);pc->data = NULL;pc->capacity = 0;pc->sz = 0;
}
好啦,到这里,今天的博主给大家带来的通讯录到这里就结束啦,如果哪里有问题,欢迎在评论区留言。如果觉得小编写的还不错的,那么可以一键三连哦,您的关注点赞和收藏是对小编最大的鼓励。谢谢大家!!!
相关文章:

通讯录的实现(超详细)——C语言(进阶)
目录 一、创建联系人信息(结构体) 二、创建通讯录(结构体) 三、define定义常量 四、打印通讯录菜单 五、枚举菜单选项 六、初始化通讯录 七、实现通讯的的功能 7.1 增加加联系人 7.2 显示所有联系人的信息 7.3 单独查…...

3D 渲染技巧-如何创建高质量写实渲染?
掌握创建高质量建筑渲染和任何 3D 渲染的艺术是一项复杂且需要技巧的工作,通常需要多年的经验和实践。实现逼真的结果需要仔细考虑众多因素,并避免可能导致缺乏真实性的假渲染效果的常见错误。 避免常见错误 - 提升渲染游戏的技巧 在追求创建真正逼真的…...
fastadmin采坑之获取当前登录admin用户的信息
在controller层里想要获取当前登录admin用户的信息 print_r($this->auth->getUserInfo());但是有个问题 我在fa_admin表中添加了新的字段,这个方法获取不到新字段的数值,具体也没有去研究估计跟方法有关 然后我直接用模型去获取数据,简…...

【Spring AOP + 自定义注解 + 动态数据源 实现主从库切换读写分离】—— 案例实战
💧 S p r i n g A O P 主从数据源切换 读写分离 自定义注解案例实战! \color{#FF1493}{Spring AOP 主从数据源切换 读写分离 自定义注解 案例实战!} SpringAOP主从数据源切换读写分离自定义注解案例实战!💧 …...

【LeetCode每日一题合集】2023.7.24-2023.7.30
文章目录 771. 宝石与石头代码1——暴力代码2——位运算集合⭐(英文字母的long集合表示) 2208. 将数组和减半的最少操作次数(贪心 优先队列)2569. 更新数组后处理求和查询⭐⭐⭐⭐⭐(线段树)TODO2500. 删除…...

《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(14)-Fiddler断点(breakpoints)实战,篡改或伪造数据
1.简介 上一篇主要就讲解和分享Fiddler断点的理论和操作,今天宏哥就用具体例子,将上一篇中的理论知识实践一下。而且在实际测试过程中,有时候需要修改请求或响应数据,或者直接模拟服务器响应,此时可以使用fiddler进行…...

ELK + Fliebeat + Kafka日志系统
参考: ELKFilebeatKafka分布式日志管理平台搭建_51CTO博客_elk 搭建 ELK 日志分析系统概述及部署(上)-阿里云开发者社区 ELK是三个开源软件的缩写,分别表示:Elasticsearch , Logstash, Kibana , 它们都是开源软件。…...

Scaling Instruction-Finetuned Language Models
Paper name Scaling Instruction-Finetuned Language Models Paper Reading Note Paper URL: https://arxiv.org/pdf/2210.11416.pdf TL;DR 2022 年谷歌出的文章,对指令微调的影响因素进行分析,提出了一些提升指令微调效果的方案。与该文章一起出品…...

rust 闭包函数
函数有自己的类型,可以像使用基础类型一样使用函数,包括将函数保存在变量中、保存在 vec 中、声明在结构体成员字段中。闭包函数也是函数,也有自己的类型定义。不过,函数实际上是指针类型,在 rust 所有权中属于借用的关…...

MySQL 实现分库和分表的备份 2023.7.29
1、分库备份 [rootlocalhost mysql-backup]# cat db_bak.sh #!/bin/bash k_userroot bak_password123456 bak_path/root/mysql-backup/ bak_cmd"-u$bak_user -p$bak_password" exc_db"Database|information_schema|mysql|performance_schema|sys" dbname…...
20230728----重返学习-跨域-模块化-webpack初步
day-122-one-hundred-and-twenty-two-20230728-跨域-模块化-webpack初步 跨域 跨域 为什么要跨域? 浏览器为了安全,不能让我们的html文件可以随意引用别的服务器中的文件,只允许我们的html或js文件中,请求我们自己服务器。这个…...
[SQL挖掘机] - 多表连接: union all
介绍: sql中的union all是用于合并两个或多个select语句的结果集的操作符。与union不同的是,union all不会自动去除重复的行,它会简单地将多个查询的结果集合并在一起,包括重复的行。 用法: union all的基本语法如下: select_…...

TypeError: run() got an unexpected keyword argument ‘hide_label‘ yolov5最新版本报错
报错展示 解决方法 把detect.py中的如上部分的 --hide-label改为 --hide-labels,成功解决....
什么是Java中的集成测试?
Java中的集成测试(Integration Test)是一种测试方法,用于测试多个模块或组件之间的交互和集成。在Java中,集成测试通常使用单元测试框架(如JUnit)编写和运行。 对于初学者来说,集成测试可能有些…...
打卡力扣题目二
#左耳听风 ARST 打卡活动重启# 目录 一、问题 二、 解题方法一 三、enumerate函数介绍 关于 ARTS 的释义 —— 每周完成一个 ARTS: ● Algorithm: 每周至少做一个 LeetCode 的算法题 ● Review: 阅读并点评至少一篇英文技术文章 ● Tips: 学习至少一个技术技巧 …...

【Qt】QML-02:QQuickView用法
1、先看demo QtCreator自动生成的工程是使用QQmlApplicationEngine来加载qml文件,下面的demo将使用QQuickView来加载qml文件 #include <QGuiApplication> #include <QtQuick/QQuickView>int main(int argc, char *argv[]) {QGuiApplication app(argc,…...

【IDEA】idea不自动生成target
文章目录 1. 不生成target2. 仅部分文件不生成target2.1. 一般原因就是资源没有设置2.2. 配置编译src/main/java文件夹下的资源文件2.3. 清理缓存(王炸) 3. 参考资料 本文描述idea不生成target的几种情况以及处理方法 1. 不生成target 像下图这样根本就…...

从官网认识 JDK,JRE,JVM 三者的关系
点击下方关注我,然后右上角点击...“设为星标”,就能第一时间收到更新推送啦~~~ JVM 是一些大厂面试必问点,要想解决 OOM、性能调优方面的问题,掌握 JVM 知识必不可少,从今天开始,将为大家介绍 JVM 的常用知…...

python 将pdf文件转图片
有小伙伴问了怎么将 pdf文件转图片的问题,我百度了一波儿,搞了以下python代码给他封装成exe工具了。 中途打包踩了个坑,python进程池的问题,本地运行没啥问题,打包好的exe文件双击就会使电脑内存爆破卡死,…...

js原型以及原型链
目录 原型隐式原型显式原型constructornew操作符 重写原型对象原型链继承原型链继承借用构造函数继承组合构造继承 原型继承寄生继承组合寄生继承 原型继承关系 原型 在JavaScript中,每个对象都有一个内置属性[[prototype]],这个属性指向一个另一个对象…...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...

Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...