通讯录实现【C语言】
目录
前言
一、整体逻辑分析
二、实现步骤
1、创建菜单和多次操作问题
2、创建通讯录
3、初始化通讯录
4、添加联系人
5、显示联系人
6、删除指定联系人
7、查找指定联系人
8、修改联系人信息
9、排序联系人信息
三、全部源码
前言
我们上期已经详细的介绍了自定义类型,本期我们来用学过的知识来做个小项目来练习练习。本期我们将用C语言实现通讯录静态版!后期介绍了动态内存管理和文件操作我们将会在此版本的基础上进行优化改进!
一、整体逻辑分析
我们还和以前实现三子棋和扫雷小项目一样,先对整体工程的逻辑尽可能详细的梳理,否则一开就是一顿狂敲,最后一走发现全是Bug这样就不好了!我们一开始尽可能的分析清楚,后面即使出现了问题也能快速确定!本期还是和往期一样采用多文件编程!OK,我们下面一起来梳理一下这个小项目的基本实现逻辑:
1、创建菜单和多次操作问题
2、创建通讯录
3、通讯录的初始化以及增删查改等操作
二、实现步骤
1、创建菜单和多次操作问题
我们期望当我们进行对通讯录操作时可以给我们在进行操作前给一个菜单功能我们操作!考虑到后面还要多次操作通讯录(增删查改等)我们结合起来很快想到用do..while循环处理最为合理,这里以前写过两三遍了我们直接上代码:
#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;do{menu();printf("请选择操作数:> ");scanf("%d", &input);switch (input){case 1://addbreak;case 2://delbreak;case 3://searchbreak;case 4://modifybreak;case 5://showbreak;case 6://sortbreak;case 0:printf("退出成功!\n");break;default:printf("选择数非法!\n");break;}} while (input);
}int main()
{test();return 0;
}
看效果:

2、创建通讯录
我们在对通讯录进行各种操作前先得有个通讯录吧!通讯录的本质还是描述一个人的属性例如:姓名、年龄、性别、电话、住址等!这些都是不同的类型,我们很容易想到用结构体就能解决!但这只是描述一个人,你的通讯录不可能只有一个人吧应该是有很多个!而这些联系人又是同一类型,因此采用结构体数组来创建通讯录!
typedef struct PeoInfo
{char name[20];int age;char sex[5];char tele[15];char addr[20];
}PeoInfo;
这就是描述一个人基本属性的结构体!当创建好了这个结构体后,我们就可以在test.c这个文件中do..while循环那里可以创建结构体数组来作为通讯录了!

但这样写会有个问题就是,当你存入或删除联系人后想打印出来看看是此时通讯录中的联系人时应该打印多少?我们还想不知道。所以这里我们还得再定义一个变量sz来记录通讯录的大小!

其实我们发现,sz不仅是可以记录通讯录的大小,而且增加联系人的时候只需要增加到sz位置即可,增加一个sz++,当删除一个sz--;这样其实 sz和通讯录是绑定在一起的,我们不妨在对通讯录进行一层封装,把他也封装成一个结构体,一个成员是PeoInfo结构体数组,一个是记录通讯录大小的sz:
typedef struct PeoInfo
{char name[20];int age;char sex[5];char tele[15];char addr[20];
}PeoInfo;typedef struct Contact
{PeoInfo data[100];int sz;
}Contact;
然后再在test.c文件中创建即可!

OK!这个创建通讯录记好了!其实这里还可已在稍微优化一下:我们发现两个结构体中都出现了大量的常数,我们万一后期需求有变,这些都得改!因此为了后期修改方便,我们把这些都改成#define的常量,后期修改起来容易!这里你可能会说上期不是介绍了枚举吗,这里正好是多个常量枚举不是更好吗?这里枚举确实可以达到目的,但枚举是在没开始前就能列举出来所有可能,我们这个可不是!所以,这里不用枚举!
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 15
#define ADDR_MAX 20
#define MAX 100typedef struct PeoInfo
{char name[NAME_MAX];int age;char sex[SEX_MAX];char tele[TELE_MAX];char addr[ADDR_MAX];
}PeoInfo;typedef struct Contact
{PeoInfo data[MAX];int sz;
}Contact;
这样才是当前逻辑下的最优代码!
3、初始化通讯录
当我们创建好了通讯录后我们先对其进行一个初始化!把结构体数组的每个元素都初始化为0,通讯录大小也置为0!因为我们要改变结构体的内容所以应该传地址!我们这里采用的是memset这个函数直接初始化为0,也可以用个循环来初始化!不了解memset的点击memset
void InitContact(Contact* pc)
{assert(pc);memset(pc->data, 0, sizeof(pc->data));pc->sz = 0;
}
4、添加联系人
我们前面已经说过了,只需要在sz的位置增加即可!但要考虑通讯录满的情况!
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");
}
OK,我们下面先来写一个显示联系人,来验证一下是否添加成功!
5、显示联系人
显示联系人就是把通讯录中的联系人打印在控制台!
void ShowContact(Contact* pc)
{assert(pc);printf("%-s\t%-s\t%-5s\t%-15s\t%-20s\n", "姓名", "年龄", "性别", "电话", "住址");for (int i = 0; i < pc->sz; i++){printf("%-s\t%-d\t%-5s\t%-15s\t%-20s\n",pc->data[i].name,pc->data[i].age,pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);}
}
OK,我们来看看是否添加成功以及显示联系人!

OK,添加联系人和显示联系人都成功了!说明我们上面逻辑没有问题!
6、删除指定联系人
我们输入要删除的人的姓名,进行查找,找到了删除,否则输出没有此联系人!注意考虑通讯录为空的情况!这里提到了查找,这个查找和通讯录的那个查找有点不一样,这个是查找后返回下标即可,那个是还要打印联系人信息功能不一样。因此我们单独在封装一个函数!
//查找要删除的人并返回下标
int FindContact(const Contact* pc, const char* name)
{for (int i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;//找到了}}return -1;//没找到
}//删除联系人
void DelContact(Contact* pc)
{assert(pc);//通讯录为空if (pc->sz == 0){printf("通讯录为空,无法删除!\n");return;}char name[NAME_MAX] = { 0 };printf("请输入要删除人的姓名:> ");scanf("%s", name);int pos = FindContact(pc, name);if (pos != -1){//删除for (int i = pos; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("成功删除联系人!\n");}else{printf("没有此联系人!\n");}
}
这里要注意的是比较字符串要用strcmp,不能用 == 这个我们在字符串函数和内存函数那一期详细介绍过!另外注意删除的时候那里下标越界问题!
看效果:
7、查找指定联系人
查找指定联系人还是先按指定方式进行查找, 找到了打印,否则输出没有此联系人!这里显示一个联系人的这个里后面修改联系人也可能要用所以我们为了方便把他封装成一个函数后面直接调用即可!
//显示单个联系人信息
void Print(const Contact* pc, int pos)
{assert(pc);printf("%-s\t%-s\t%-5s\t%-15s\t%-20s\n", "姓名", "年龄", "性别", "电话", "住址");printf("%-s\t%-d\t%-5s\t%-15s\t%-20s\n",pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr);}//查找指定联系人
void Search(const Contact* pc)
{assert(pc);char name[NAME_MAX] = { 0 };printf("请输入要查找联系人姓名:> ");scanf("%s", name);int pos = -1;//初始化为-1表示没有此联系人for (int i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){pos = i;//找到了break;}}if (pos != -1){Print(pc, pos);}else{printf("没有此联系人!\n");}
}
其实还可以在简洁一点,上面已经实现了FindContact我们可以用一下:
void Search(const Contact* pc)
{assert(pc);char name[NAME_MAX] = { 0 };printf("请输入要查找联系人姓名:> ");scanf("%s", name);int pos = FindContact(pc, name);if (pos != -1){Print(pc, pos);}else{printf("没有此联系人!\n");}
}
看效果:

8、修改联系人信息
修改联系人信息这块就有很多可能,比如只修改一项例如名字或性别等,也有可能修改多项例如名字、年龄等,也有可能得全部修改这几种情况。当我们要修改之前群殴我们能还是期望有一个菜单供我们选择,当修改完一项后我们可能还要继续修改。基于此我们采用和test.c文件中得do...while循环+菜单!
//修改联系人信息菜单
void menu1()
{system("cls");printf("*****************************************\n");printf("********* 1. name 2. age **********\n");printf("********* 3. sex 4. tele **********\n");printf("********* 5. addr 6. all **********\n");printf("********* 0. exit **********\n");printf("*****************************************\n");
}//修改名字
void ModName(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人姓名:> ");scanf("%s", pc->data[ret].name);printf("修改成功!\n");
}//修改年龄
void ModAge(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人年龄:> ");scanf("%d", &pc->data[ret].age);printf("修改成功!\n");
}//修改性别
void ModSex(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人性别:> ");scanf("%s", &pc->data[ret].sex);printf("修改成功!\n");
}//修改电话
void ModTele(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人电话:> ");scanf("%s", &pc->data[ret].tele);printf("修改成功!\n");
}//修改住址
void ModAddr(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人住址:> ");scanf("%s", &pc->data[ret].addr);printf("修改成功!\n");
}void ModAll(Contact* pc, int ret)
{assert(pc);printf("请输入联系人姓名:> ");scanf("%s", pc->data[ret].name);printf("请输入联系人年龄:> ");scanf("%d", &pc->data[ret].age);printf("请输入联系人性别:> ");scanf("%s", pc->data[ret].sex);printf("请输入联系人电话:> ");scanf("%s", pc->data[ret].tele);printf("请输入联系人住址:> ");scanf("%s", pc->data[ret].addr);printf("修改成功!\n");
}//修改联系人
void ModContact(Contact* pc)
{assert(pc);//判断是否为空if (pc->sz == 0){printf("通讯录为空,无法修改!\n");return;}char name[NAME_MAX] = { 0 };printf("请输入要修改系人姓名:> ");scanf("%s", name);//判断是否有该联系人int ret = FindContact(pc, name);//有该联系人if (ret != -1){int n = 0;do{menu1();printf("请选择修改内容:> ");scanf("%d", &n);//修改switch (n){case 1:ModName(pc,ret);break;case 2:ModAge(pc, ret);break;case 3:ModSex(pc, ret);break;case 4:ModTele(pc, ret);break;case 5:ModAddr(pc, ret);break;case 6:ModAll(pc, ret);break;case 0:printf("修改结束!\n");break;default:printf("选择数非法!\n");break;}} while (n);}else{printf("没有此联系人!\n");}
}
看效果:(小编做了清屏)
修改前:


修改后:

OK,还是比较成功的!我们再来进行对他实现一下排序!
9、排序联系人信息
我们上面已经实现了通讯录的增删查改的基本功能!我们想让他再有一个排序功能,比如按名字排序,或年龄排序!我们在指针进阶那块介绍过回调函数,这里我们用qsort进行对通讯录排序!我们期望还是和上面一样一开始有个菜单选择!
//排序菜单
void menu2()
{system("cls");printf("**********************************\n");printf("***** 1. name 2. age **********\n");printf("**********************************\n");
}//名字比较函数
int cmp_name(const void* str1, const void* str2)
{//return strcmp(((PeoInfo*)str1)->name, ((PeoInfo*)str2)->name);return strcmp((((Contact*)str1)->data)->name, (((Contact*)str2)->data)->name);
}//名字排序
void SortName(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_name);
}//年龄比较函数
int cmp_age(const void* str1, const void* str2)
{//return ((PeoInfo*)str1)->age - ((PeoInfo*)str2)->age;return (((Contact*)str1)->data)->age - (((Contact*)str2)->data)->age;
}
//年龄排序
void SortAge(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_age);
}//排序联系人信息
void SortContact(Contact* pc)
{assert(pc);//判断为空if (pc->sz == 0){printf("通讯录为空,无法排序!\n");Sleep(3000);system("cls");return;}menu2();int n = 0;printf("请选择排序方式:> ");scanf("%d", &n);//排序switch (n){case 1:SortName(pc);system("cls");printf("排序成功!\n");break;case 2:SortAge(pc);system("cls");printf("排序成功!\n");break;default:printf("选择数非法!\n");Sleep(3000);system("cls");break;}
}
看效果:
排序前(年龄):

排序后(年龄):

排序前(名字):

排序后(名字):

OK,实现了排序!
三、全部源码
contact.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<windows.h>#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 15
#define ADDR_MAX 20
#define MAX 100typedef struct PeoInfo
{char name[NAME_MAX];int age;char sex[SEX_MAX];char tele[TELE_MAX];char addr[ADDR_MAX];
}PeoInfo;typedef struct Contact
{PeoInfo data[MAX];int sz;
}Contact;//初始化通讯录
void InitContact(Contact* pc);//添加联系人
void AddContact(Contact* pc);//显示联系人
void ShowContact(const Contact* pc);//删除联系人
void DelContact(Contact* pc);//查找指定联系人
void Search(const Contact* pc);//修改联系人
void ModContact(Contact* pc);//排序联系人信息
void SortContact(Contact* pc);
contact.c
#include"contact.h"//初始化通讯录
void InitContact(Contact* pc)
{assert(pc);memset(pc->data, 0, sizeof(pc->data));pc->sz = 0;
}//增加联系人
void AddContact(Contact* pc)
{assert(pc);//通讯录已满if (pc->sz == MAX){printf("通讯录已满,无法添加!\n");Sleep(2000);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");Sleep(1000);
}//显示联系人
void ShowContact(const Contact* pc)
{assert(pc);printf("%-s\t%-s\t%-5s\t%-15s\t%-20s\n", "姓名", "年龄", "性别", "电话", "住址");for (int i = 0; i < pc->sz; i++){printf("%-s\t%-d\t%-5s\t%-15s\t%-20s\n",pc->data[i].name,pc->data[i].age,pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);}
}//查找要删除的人并返回下标
int FindContact(const Contact* pc, const char* name)
{for (int i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;//找到了}}return -1;//没找到
}//删除联系人
void DelContact(Contact* pc)
{assert(pc);if (pc->sz == 0){printf("通讯录为空,无法删除!\n");Sleep(2000);return;}char name[NAME_MAX] = { 0 };printf("请输入要删除人的姓名:> ");scanf("%s", name);int pos = FindContact(pc, name);if (pos != -1){//删除for (int i = pos; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("成功删除联系人!\n");Sleep(1000);}else{printf("没有此联系人!\n");Sleep(2000);}
}//显示单个联系人信息
void Print(const Contact* pc, int pos)
{assert(pc);printf("%-s\t%-s\t%-5s\t%-15s\t%-20s\n", "姓名", "年龄", "性别", "电话", "住址");printf("%-s\t%-d\t%-5s\t%-15s\t%-20s\n",pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr);}//查找指定联系人
void Search(const Contact* pc)
{assert(pc);char name[NAME_MAX] = { 0 };printf("请输入要查找联系人姓名:> ");scanf("%s", name);int pos = FindContact(pc, name);if (pos != -1){Print(pc, pos);}else{printf("没有此联系人!\n");Sleep(2000);system("cls");}
}//修改联系人信息菜单
void menu1()
{system("cls");printf("*****************************************\n");printf("********* 1. name 2. age **********\n");printf("********* 3. sex 4. tele **********\n");printf("********* 5. addr 6. all **********\n");printf("********* 0. exit **********\n");printf("*****************************************\n");
}//修改名字
void ModName(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人姓名:> ");scanf("%s", pc->data[ret].name);
}//修改年龄
void ModAge(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人年龄:> ");scanf("%d", &pc->data[ret].age);
}//修改性别
void ModSex(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人性别:> ");scanf("%s", &pc->data[ret].sex);
}//修改电话
void ModTele(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人电话:> ");scanf("%s", &pc->data[ret].tele);
}//修改住址
void ModAddr(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人住址:> ");scanf("%s", &pc->data[ret].addr);
}void ModAll(Contact* pc, int ret)
{assert(pc);printf("请输入联系人姓名:> ");scanf("%s", pc->data[ret].name);printf("请输入联系人年龄:> ");scanf("%d", &pc->data[ret].age);printf("请输入联系人性别:> ");scanf("%s", pc->data[ret].sex);printf("请输入联系人电话:> ");scanf("%s", pc->data[ret].tele);printf("请输入联系人住址:> ");scanf("%s", pc->data[ret].addr);
}//修改联系人
void ModContact(Contact* pc)
{assert(pc);//判断是否为空if (pc->sz == 0){printf("通讯录为空,无法修改!\n");Sleep(2000);system("cls");return;}char name[NAME_MAX] = { 0 };printf("请输入要修改系人姓名:> ");scanf("%s", name);//判断是否有该联系人int ret = FindContact(pc, name);//有该联系人if (ret != -1){int n = 0;do{menu1();Print(pc, ret);printf("请选择修改内容:> ");scanf("%d", &n);//修改switch (n){case 1:ModName(pc,ret);break;case 2:ModAge(pc, ret);break;case 3:ModSex(pc, ret);break;case 4:ModTele(pc, ret);break;case 5:ModAddr(pc, ret);break;case 6:ModAll(pc, ret);break;case 0:printf("修改结束!\n");Sleep(2000);system("cls");break;default:printf("选择数非法!\n");Sleep(2000);system("cls");break;}} while (n);}else{printf("没有此联系人!\n");Sleep(2000);system("cls");}
}//排序菜单
void menu2()
{system("cls");printf("**********************************\n");printf("***** 1. name 2. age **********\n");printf("**********************************\n");
}//名字比较函数
int cmp_name(const void* str1, const void* str2)
{//return strcmp(((PeoInfo*)str1)->name, ((PeoInfo*)str2)->name);return strcmp((((Contact*)str1)->data)->name, (((Contact*)str2)->data)->name);
}//名字排序
void SortName(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_name);
}//年龄比较函数
int cmp_age(const void* str1, const void* str2)
{//return ((PeoInfo*)str1)->age - ((PeoInfo*)str2)->age;return (((Contact*)str1)->data)->age - (((Contact*)str2)->data)->age;
}
//年龄排序
void SortAge(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_age);
}//排序联系人信息
void SortContact(Contact* pc)
{assert(pc);//判断为空if (pc->sz == 0){printf("通讯录为空,无法排序!\n");Sleep(3000);system("cls");return;}menu2();int n = 0;printf("请选择排序方式:> ");scanf("%d", &n);//排序switch (n){case 1:SortName(pc);system("cls");printf("排序成功!\n");break;case 2:SortAge(pc);system("cls");printf("排序成功!\n");break;default:printf("选择数非法!\n");Sleep(3000);system("cls");break;}
}
test.c
#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 1:AddContact(&con);system("cls");break;case 2:DelContact(&con);system("cls"); break;case 3:system("cls");Search(&con);break;case 4:ModContact(&con);break;case 5:system("cls");ShowContact(&con);break;case 6:SortContact(&con);break;case 0:printf("退出成功!\n");break;default:printf("选择数非法!\n");break;}} while (input);
}int main()
{test();return 0;
}
OK,本期分享就到这里!好兄弟,我们下期再见!
相关文章:
通讯录实现【C语言】
目录 前言 一、整体逻辑分析 二、实现步骤 1、创建菜单和多次操作问题 2、创建通讯录 3、初始化通讯录 4、添加联系人 5、显示联系人 6、删除指定联系人 7、查找指定联系人 8、修改联系人信息 9、排序联系人信息 三、全部源码 前言 我们上期已经详细的介绍了自定…...
pcl欧式聚类
欧式聚类实现方法大致是: 1、找到空间中某点 p 1 p_1 p1,用KD-Tree找到离他最近的n个点,判断这n个点到 p 1 p_1 p1的距离。将距离小于阈值r的点 p 2 、 p 3 、 p 4 p_2、p_3、p_4 p2、p3、p4…放在类Q里 2、在 Q ( p 1 ) Q(p_1…...
macOS Ventura 13.5.1(22G90)发布(附黑/白苹果系统镜像地址)
系统镜像下载:百度:黑果魏叔 系统介绍 黑果魏叔 8 月 18 日消息,苹果今日向 Mac 电脑用户推送了 macOS 13.5.1 更新(内部版本号:22G90),本次更新距离上次发布隔了 24 天。 本次更新重点修复了…...
分布式监控平台——Zabbix
市场上常用的监控软件: 传统运维:zabbix、 Nagios 一、zabbix概述 作为一个运维,需要会使用监控系统查看服务器状态以及网站流量指标,利用监控系统的数据去了解上线发布的结果,和网站的健康状态。 利用一个优秀的监…...
【OpenGauss源码学习 —— 列存储(创建表)】
列存储 什么是列存储?语法实现语法格式参数说明示例源码分析(创建表)语法层(Gram.y)子模块(utility.cpp) 总结 声明:本文的部分内容参考了他人的文章。在编写过程中,我们…...
Jenkins 监控dist.zip文件内容发生变化 触发自动部署
为Jenkins添加plugin http://xx:xx/manage 创建一个任务 构建触发器 每3分钟扫描一次,发现指定文件build.zip文件的MD5发生变化后 触发任务...
Linux系列讲解 —— FTP协议的应用
简单介绍一下FTP文件传输协议在linux系统中的应用。 目录 0. 基本概念1. FTP Server1.1 安装FTP Server1.2 FTP Server开启和关闭1.3 查看FTP Server是否开启1.4 FTP服务器配置 2. FTP Client2.1 lftp2.2 ftp2.3 sftp2.4 文件资源管理器集成的ftp和sftp 3. ftp常用命令 0. 基本…...
Rancher-RKE-install 部署k8s集群
一、为什么用Rancher-RKE-install 1.CNCF认证的k8s安装程序。 2.有中文文档。 二、安装步骤 1.下载Rancher-Rke的二进制包-下面是项目的地址 GitHub - rancher/rke: Rancher Kubernetes Engine (RKE), an extremely simple, lightning fast Kubernetes distrib…...
PHP8的正则表达式-PHP8知识详解
在网页程序的时候,经常会有查找符合某些复杂规则的字符串的需求。正则表达式就是描述这些规则的工具。 正则表达式是把文本或者字符串按照一定的规范或模型表示的方法,经常用于文本的匹配操作。 例如:我们在填写手机号码的时候,…...
SpringCloud实用篇7——深入elasticsearch
目录 1 数据聚合1.1 聚合的种类1.2 DSL实现聚合1.2.1 Bucket聚合语法1.2.2 聚合结果排序1.2.3 限定聚合范围1.2.4 Metric聚合语法1.2.5.小结 1.3 RestAPI实现聚合1.3.1 API语法1.3.2 业务需求1.3.3 业务实现 2 自动补全2.1 拼音分词器2.2 自定义分词器2.3 自动补全查询2.4 实现…...
uni-app 经验分享,从入门到离职(二)—— tabBar 底部导航栏实战篇
文章目录 📋前言⏬关于专栏 🎯关于小程序 tabbar 的一些知识🎯创建一个基本的 tabBar📝最后 📋前言 这篇文章的内容主题是关于小程序的 tabBar 底部导航栏的入门使用和实战技巧。通过上一篇文章的基础,我们…...
Java虚拟机(JVM):内存区域
一、内存区域介绍 Java虚拟机(JVM)内存可以分为以下几个区域: 程序计数器(Program Counter Register):用于记录当前线程执行的字节码指令的地址,属于线程私有的区域。在任意时刻,一…...
11 - git stash 开发中临时加塞了紧急任务怎么处理
查看所有文章链接:(更新中)GIT常用场景- 目录 文章目录 开发中临时加塞了紧急任务怎么处理 开发中临时加塞了紧急任务怎么处理 当你此时工作区已经修改了 Readme 文件,然后突然需要解决其他问题(紧急问题、新任务&…...
高效的WMS系统手持盘点方案
WMS系统手持盘点就是指利用WMS系统支持的手持式电子盘点设备进行库存盘点的方式。 具体来说: - 手持盘点设备是一种小型的电子设备,具有移动条形码扫描功能,可以实时与WMS系统联通。 - WMS系统利用手持设备,可以给仓储人员下发具体的盘点任务,例如需要盘点的货位、商品等信息…...
Oracle分页技术
1、使用两层嵌套 SELECT *FROM (SELECT A.*, ROWNUM RNFROM (SELECT * FROM edw_t100_bal_all) AWHERE ROWNUM < 40)WHERE RN > 21; 2、使用between..and.. SELECT *FROM (SELECT A.*, ROWNUM RN FROM (SELECT * FROM edw_t100_bal_all) A)WHERE RN between 21 and 40…...
2023-08-15 Untiy进阶 C#知识补充6——C#7主要功能与语法
文章目录 一、字面值改进二、out 内部声明 / 弃元三、ref 返回值四、本地函数五、抛出表达式六、元组七、模式匹配 注意:在此仅提及 Unity 开发中会用到的一些功能和特性,对于不适合在 Unity 中使用的内容会忽略。 C# 7 对应 Unity 版本࿱…...
logstash配置文件
input { kafka { topics > “xxxx” bootstrap_servers > “ip:port” auto_offset_reset > “xxxx” group_id > “xxxx” consumer_threads > 3 codec > “json” } } filter { grok { match > { “message” > ‘%{IP:client_ip} - - [%{HTTPDATE:…...
docker搭建lnmp(nginx+mysql57+php7.4)
docker搭建lnmp(nginxmysql57php7.4) 1. 准备工作 docker安装 $ sudo yum update $ sudo sudo yum install -y yum-utils device-mapper-persistent-data lvm2 $ sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo $ sudo…...
专访 BlockPI:共建账户抽象未来的新一代 RPC 基础设施
在传统 RPC 服务板块上,开发者一直饱受故障风险、运行环境混乱等难题的折磨。实现 RPC 服务的去中心化,且保持成本优势和可扩展性,始终是区块链基础设施建设的重要命题之一。从 2018 年观察中心化 RPC 供应商服务现状开始,BlockPI…...
js fetch请求中断的几种方式
1、通过AbortController 这是官方标准手段,真正意义的阻止请求(不支持ie) 后端接口设置的两秒返回数据 function myFetch() {const controller new AbortController();const signal controller.signal;fetch(http://localhost:3000/aaa/…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
