C语言实现——简易通讯录

news2024/12/24 10:03:34

前言:小伙伴们又见面啦!这几天通过我们对自定义数据类型的学习,我们已经掌握了如何同时对多种数据类型进行管理,那么今天这篇文章,我们就来干一件大事——实现简易的通讯录


一.思路分析

先来想想通讯录有哪些功能:

添加联系人信息

删除联系人信息

查找联系人信息

修改联系人信息

显示联系人信息

排序联系人信息

清空所有联系人

基本上都会有以上的7种功能,而且我们需要一个菜单来让用户进行选择。

制作一个大的项目,往往都要将代码写得规范整洁

所以我们要跟之前写三子棋和扫雷游戏一样,将宏定义常量,函数声明与定义,以及主函数分开来写,方便日后对代码的维护

Contact即为联系人,通讯录。

Contact.h        用来管理宏定义常量、函数的声明。

Contact.c        是管理函数的定义实现。

test.c               是用来进行测试代码的功能。


 二.基本框架实现

下面我们就先来一步一步的实现通讯录的基本框架

1.目录

#include "Contact.h"
void menu()
{
	printf("*********************************\n");
	printf("***** 1.add        2.del    *****\n");
	printf("***** 3.search     4.revize *****\n");
	printf("***** 5.show       6.sort   *****\n");
	printf("***** 7.empty      0.exit   *****\n");
	printf("*********************************\n");
}
int main()
{
	int input;
	do {
		menu();
		printf("请选择->:");
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			printf("退出通讯录\n");
			break;
		case 1:
			break;
		case 2:
			break;
		case 3:
			break;
		case 4:
			break;
		case 5:
			break;
		case 6:
			break;
		case 7:
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
	} while (input);
	return 0;
}

目录的做法对于现在的我们来说应该算是手到擒来,但是现在我们想要进行一个小小的改进:

如果我们做这个代码要交给其他的程序员看的话,他可能看到Switch-case语句时会很难分辨出各个case语句的数字都代表的是哪一项功能,还得不停地回到菜单函数去查看。

所以我们希望用各项功能的名字取代数字,这样就会更加方便。

那么这时候,就要用到枚举啦:

#include "Contact.h"
void menu()
{
	printf("*********************************\n");
	printf("***** 1.add        2.del    *****\n");
	printf("***** 3.search     4.revize *****\n");
	printf("***** 5.show       6.sort   *****\n");
	printf("***** 7.empty      0.exit   *****\n");
	printf("*********************************\n");
}
enum Function
{
    EXIT,//默认从0开始
    ADD,
    DEL,
    SEARCH,
    REVIZE,
    SHOW,
    SORT,
    EMPTY,
};
int main()
{
	int input;
	do {
		menu();
		printf("请选择->:");
		scanf("%d", &input);
		switch (input)
		{
		case EXIT:
			printf("退出通讯录\n");
			break;
		case ADD:
			break;
		case DEL:
			break;
		case SEARCH:
			break;
		case REVIZE:
			break;
		case SHOW:
			break;
		case SORT:
			break;
		case EMPTY:
			break;
		default:
			printf("输入错误,请重新输入\n");

		}
	} while (input);
	return 0;
}

枚举常量默认从0开始,所以将exit放在第一位,而后逐个递增。

最后再将数字进行替换就好啦。


2.创建联系人

通讯录里会保存联系人的哪些信息呢???

名字、电话、住址等等,这些显然不会是一种数据类型

所以创建联系人变量,就要用到结构体啦。

struct Peomessage
{
	char name[20];
	int tele[12];
	char addr[30];
};

简单创建一个联系人信息的结构体,这时候又会有问题:

一个人的电话的不会超过12个数字,但是名字不会超过20个字符吗???

某一天,我认识了一个外国朋友,它的名字特别长,就要修改name数组的大小,地址同样如此

那么为了方便高效,我们将这两个数组的大小进行宏定义

#define NAME_MAX 20
#define ADDR_MAX 30
typedef struct Peomessage
{
	char name[NAME_MAX];
	int tele[12];
	char addr[ADDR_MAX];
}Peomessage;

 同时我们使用typedef类型重定义关键字将此结构体类型的名字定义为Peomessage,方便我们后续的使用。

创建完联系人类型之后呢,我们就要开始创建联系人列表,这显然需要一个结构体类型的数组

那么数组的大小是多少呢???这个又是一个无法估量的问题,所以我们仍然使用宏定义常量

先默认能够存放100个联系人。

Peomessage data[DATA_MAX];

int sz;

 我们建立了这样一个数组。除此之外,我们还需要一个整型变量来帮助我统计通讯录里有多少个联系人了,所以我们定义sz

不难看出,这两者我们也需要同时管理,所以干脆就将它们两个也用一个结构体来管理

typedef struct Contact
{
	Peomessage data[DATA_MAX];
	int sz;
}Contect;

3.初始化数据

我们创建了Contect结构体之后,还需要将其内部数据初始化为0,以免出现异常情况。

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

memset是我们之前讲过的内存函数,可以将任意位置的任意数量的数据设置成我们想要的值

这里要讲的一点是assert函数,这个函数的作用就是帮助我们检查代码是否有错,有错误则会立即终止程序并返回错误信息。

这里主要是帮助我们判断pc指针的可用性,如果pc指针不可用,那么就会造成巨大的问题。

通过上述的步骤,我们已经实现了通讯录的基本框架下面我们开始实现各种功能。 


三.功能实现

每一个功能的实现,必然少不了对于函数的运用。

1.添加联系人信息

再添加信息之前,有一点非常值得注意:那就是我们的通讯录有没有存满

所以我们得先进行一个判断。

void AddContact(Contact* pc)
{
	assert(pc);
	//判断是否已存满
	if (pc->sz == DATA_MAX)
	{
		printf("通讯录已满,无法添加\n");
		return;
	}
	//没有存满则进行存放
	printf("请输入名字:");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入电话:");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入住址:");
	scanf("%s", pc->data[pc->sz].addr);
	pc->sz++;
	printf("添加成功\n");
}

添加过一个联系人之后,我们的sz就需要+1,用来记录我们已经存放了多少个联系人的信息。

来看实践:

添加完联系人之后,我们想马上看一下我们到底有没有存进去

下面我们就实现显示联系人信息


2.显示联系人信息

我们这里要解释一点,显示联系人信息是显示当前已经存放过的所有联系人的信息

而之后要讲的查找联系人信息才是针对某个联系人来显示

那么既然要打印所有人的信息,就必然少不了对于循环的运用。

同样的,在显示之前,我们还要判断一下通讯录是否为空

void ShowContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空\n");
		return;
	}
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%s %s %s\n", pc->data[i].name, pc->data[i].tele, pc->data[i].addr);
	}
}

这样我们就可以显示出我们的联系人信息啦。

但是发现我们的信息上边没有像名字,电话,住址这样的列表名,而且我们每一行的信息排列并不整齐

所以我们进行几处修改和补充:

void ShowContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空\n");
		return;
	}
	printf("%-20s%-12s%-30s\n","姓名","电话","住址");
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s%-12s%-30s\n", pc->data[i].name, pc->data[i].tele, pc->data[i].addr);
	}
}

这种打印方式不知道小伙伴们了不了解:

%s是我们常规的方式,那么%20s就是指在打印的数据之前先打印20个空格

%-20s则代表着先打印数据,在打印20个空格,这样我们就能实现对齐啦。


3.查找联系人信息

这里我们先讲查找联系人信息

因为删除联系人信息、修改联系人信息实现的前期是:我们必须得先找到这个人才行。

 那么我们该怎么查找呢???显然通过名字是最直接的方法。而且我们需要用到循环

既然使用名字查找这种方式到处都要用到,所以我们干脆给它也写成一个函数

int FindByName(Contact* pc, char* name)
{
	assert(pc && name);
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(name, pc->data[i].name) == 0)
		{
			return i;
		}
	}
	return -1;
}

这里我们这个函数的返回值为什么要是整型呢???

而且要是找到的话还返回它在通讯录列表里的位置,往下看就知道啦。

void SearchContact(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX];
	printf("请输入要查找人的名字:");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("找不到该联系人\n");
		return;
	}
	printf("%-20s%-12s%-30s\n", "姓名", "电话", "住址");
	printf("%-20s%-12s%-30s\n", pc->data[ret].name, pc->data[ret].tele, pc->data[ret].addr);
}

 通过定义ret来接收FindByName函数的返回值,这样我们就能得到要查找的联系人的位置,能够轻松的打印出他的信息啦。

 


4.删除联系人信息

删除某个联系人信息,必然也是要先查找一下这个联系人存不存在

同时想要删除一个联系人信息,必然是通过他的名字来删除

void DelContact(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX];
	printf("请输入要查找人的名字:");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("找不到该联系人\n");
		return;
	}
}

接下来就是要思考怎么来删除了。

我们使用的通讯录本质上是一个数组,想要删除某个位置的信息,首先想到的就是把它置为0

但是这样一来我们就会浪费一个空间因为你不知道这个位置的数组下标,也不可能把一个新的联系人信息补充到这个位置上来

所以我们采取逐个覆盖的方法,用后边的信息逐一向前一位进行覆盖

void DelContact(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX];
	printf("请输入要删除联系人的名字:");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("该联系人信息不存在\n");
		return;
	}
    //删除联系人
	int i = 0;
	for (i = ret;i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}

用 i 接收 FindByName 的返回值 ret ,这样我们就得到了要删除的联系人的位置,从这里开始我们通过循环进行逐一覆盖。

删除一个联系人之后,不要忘记我们的sz要-1

 


5.修改联系人信息

修改联系人信息之前,我们还是要先查找一下这个联系人存不存在,如果存在则进行修改。

修改联系人信息则需要让用户选择出要具体修改哪个信息

所以我们还需要一个小目录来供用户选择,这里的操作就和刚开始的制作目录的方法一样啦。

void menu2()
{
	printf("*******************************\n");
	printf("***** 0.exit       1.name *****\n");
	printf("***** 2.tele       3.addr *****\n");
	printf("*******************************\n");
}
enum Type
{
	QUIT,
	NAME,
	TELE,
	ADDR
};

这里为了不发生类型多次重定义,我们将“退出”用它的另一个英文“quit”来定义。

void ReviseContact(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX];
	printf("请输入要修改联系人的名字:");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("该联系人信息不存在\n");
		return;
	}
	int input;
	do
	{
		menu2();
		printf("请选择要修改的信息类型:");
		scanf("%d", &input);
		switch (input)
		{
		case QUIT:
			printf("退出修改\n");
			break;
		case NAME:
			printf("请输入名字:");
			scanf("%s", pc->data[ret].name);
			printf("修改成功\n");
			break;
		case TELE:
			printf("请输入电话:");
			scanf("%s", pc->data[ret].tele);
			printf("修改成功\n");
			break;
		case ADDR:
			printf("请输入地址:");
			scanf("%s", pc->data[ret].addr);
			printf("修改成功\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}

 来看实战:


 6.排序联系人信息

排序联系人信息我们这里需要用到一个函数——qsort

使用这个函数需要头文件:#include<stdlib.h>

 qsort函数有4个参数,分别是:

base        排序的首地址

num         排序的数据数量

size          排序的数据字节大小

compar    排序的方式函数  

 通讯录的排序都是通过名字的首字母来排序的,所以这里我们要比较的是两个名字的字符串

所以我们强制类型转换之后要调用name成员来比较。

int compare(const void* a, const void* b)
{
	return strcmp(((Peomessage*)a)->name, ((Peomessage*)b)->name);
}

这里的强制类型转换用法我们在之前的文章——内存函数中已经讲到,不熟悉的小伙伴建议再去补习补习哦。

void SortContact(Contact* pc)
{
	assert(pc);
	qsort(pc->data, pc->sz, sizeof(pc->data[0]), compare);
	printf("排序成功\n");
}

我们要排序的是data这个结构体类型的数组,所以要传入它的首地址、大小、以及每个结构体数据的字节大小,最后传入我们的比较函数compare

来看实战:


7.清空所有联系人

那么最后,如何清空所有的联系人呢???

其实这个超级简单,所谓清空,不就是再初始化一次吗,我们一开始就已经搞定啦。

void EmptyContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data));
}


 四.完整代码展示

1.Contact.h

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

#define NAME_MAX 20
#define ADDR_MAX 30
#define DATA_MAX 100 
//选项
enum Function
{
	EXIT,//默认从0开始
	ADD,
	DEL,
	SEARCH,
	REVISE,
	SHOW,
	SORT,
	EMPTY
};
enum Type
{
	QUIT,
	NAME,
	TELE,
	ADDR
};
//联系人信息
typedef struct Peomessage
{
	char name[NAME_MAX];
	char tele[12];
	char addr[ADDR_MAX];
}Peomessage;
typedef struct Contact
{
	Peomessage data[DATA_MAX];
	int sz;
}Contact;
//初始化通讯录
void InitContact(Contact* pc);
//增加联系人信息
void AddContact(Contact* pc);
//显示联系人信息
void ShowContact(Contact* pc);
//查找联系人信息
void SearchContact(Contact* pc);
//删除联系人信息
void DelContact(Contact* pc);
//修改联系人信息
void ReviseContact(Contact* pc);
//排序联系人信息
void SortContact(Contact* pc);
//清空所有联系人
void EmptyContact(Contact* pc);

2.Contact.c

#include"Contact.h"
//初始化通讯录
void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data));
}
//增加联系人信息
void AddContact(Contact* pc)
{
	assert(pc);
	//判断是否已存满
	if (pc->sz == DATA_MAX)
	{
		printf("通讯录已满,无法添加\n");
		return;
	}
	//没有存满则进行存放
	printf("请输入名字:");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入电话:");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入住址:");
	scanf("%s", pc->data[pc->sz].addr);
	pc->sz++;
	printf("添加成功\n");
}
//显示联系人信息
void ShowContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空\n");
		return;
	}
	printf("%-20s%-12s%-30s\n","姓名","电话","住址");
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s%-12s%-30s\n", pc->data[i].name, pc->data[i].tele, pc->data[i].addr);
	}
}
//通过名字查找
int FindByName(Contact* pc, char* name)
{
	assert(pc && name);
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(name, pc->data[i].name) == 0)
		{
			return i;
		}
	}
	return -1;
}
//查找联系人信息
void SearchContact(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX];
	printf("请输入要查找人的名字:");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("找不到该联系人\n");
		return;
	}
	printf("%-20s%-12s%-30s\n", "姓名", "电话", "住址");
	printf("%-20s%-12s%-30s\n", pc->data[ret].name, pc->data[ret].tele, pc->data[ret].addr);
}
//删除联系人信息
void DelContact(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX];
	printf("请输入要删除联系人的名字:");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("该联系人信息不存在\n");
		return;
	}
	//删除联系人
	int i = 0;
	for (i = ret;i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}
void menu2()
{
	printf("*******************************\n");
	printf("***** 0.exit       1.name *****\n");
	printf("***** 2.tele       3.addr *****\n");
	printf("*******************************\n");
}
//修改联系人信息
void ReviseContact(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX];
	printf("请输入要修改联系人的名字:");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("该联系人信息不存在\n");
		return;
	}
	int input;
	do
	{
		menu2();
		printf("请选择要修改的信息类型:");
		scanf("%d", &input);
		switch (input)
		{
		case QUIT:
			printf("退出修改\n");
			break;
		case NAME:
			printf("请输入名字:");
			scanf("%s", pc->data[ret].name);
			printf("修改成功\n");
			break;
		case TELE:
			printf("请输入电话:");
			scanf("%s", pc->data[ret].tele);
			printf("修改成功\n");
			break;
		case ADDR:
			printf("请输入地址:");
			scanf("%s", pc->data[ret].addr);
			printf("修改成功\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}
//排序联系人信息
int compare(const void* a, const void* b)
{
	return strcmp(((Peomessage*)a)->name, ((Peomessage*)b)->name);
}
void SortContact(Contact* pc)
{
	assert(pc);
	qsort(pc->data, pc->sz, sizeof(pc->data[0]), compare);
	printf("排序成功\n");
}
//清空所有联系人
void EmptyContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data));
	printf("已清空通讯录\n");
}

3.test.c

#include "Contact.h"
void menu1()//菜单
{
	printf("*********************************\n");
	printf("***** 1.add        2.del    *****\n");
	printf("***** 3.search     4.revise *****\n");
	printf("***** 5.show       6.sort   *****\n");
	printf("***** 7.empty      0.exit   *****\n");
	printf("*********************************\n");
}
int main()
{
	Contact con;
	//初始化通讯录
	InitContact(&con);
	int input;
	do {
		menu1();
		printf("请选择->:");
		scanf("%d", &input);
		switch (input)
		{
		case EXIT:
			printf("退出通讯录\n");
			break;
		case ADD:
			AddContact(&con);
			break;
		case DEL:
			DelContact(&con);
			break;
		case SEARCH:
			SearchContact(&con);
			break;
		case REVISE:
			ReviseContact(&con);
			break;
		case SHOW:
			ShowContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case EMPTY:
			EmptyContact(&con);
			break;
		default:
			printf("输入错误,请重新输入\n");

		}
	} while (input);
	return 0;
}

五.总结

到这里,对于如何用C语言实现简易通讯录的讲解终于完结撒花啦!!!

喜欢博主文章的小伙伴一定要点个关注不迷路哦。

最后还是要记得一键三连呀!

祝大家中秋节快乐,我们下期再见啦!

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

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

相关文章

Spring Boot:利用JPA进行数据库的查删

目录标题 DAO 、Service 、 Controller 层控制器文件示例代码-单个查找查找成功示例代码-列表查找查找成功示例代码-删除删除成功 DAO 、Service 、 Controller 层 DAO 层负责数据库访问&#xff0c;它封装了对数据库的访问操作&#xff0c;例如查询、插入、更新和删除等。 Q…

《向量数据库指南》——向量数据库Elasticsearch -> Milvus 2.x

Elasticsearch -> Milvus 2.x 1. 准备 ES 数据 要迁移 ES 数据,前提假设您已经拥有属于自己的 es Server(自建、ElasticCloud、阿里云 ES 等),向量数据存储在 dense_vector,以及其他字段在 index 中,index mapping 形式如: 2. 编译打包 首先下载迁移项目源码:https…

第P7周—咖啡豆识别(1)

数据集及wen件目录介绍&#xff1a; 数据集&#xff1a;工作台 - Heywhale.com 一、前期工作 1.1 数据详情 import torch import torch.nn as nn import torchvision.transforms as transforms import torchvision from torchvision import transforms, datasets import os,…

Ros2 学习02- ubuntu22.04 安装ros2

设置镜像源 sudo vim /etc/apt/sources.list#阿里源 deb http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ jamm…

小波变换学习笔记【1】

【声明】本博客为学习B站视频小波分解与重构所做笔记&#xff0c;供自己和大家查阅学习&#xff0c;想查看 up 原视频请移步 B 站&#xff0c;侵删。 1.1 小波变换的由来 傅里叶变换基本思想&#xff1a;将信号分解成一系列不同频率的连续正弦波的叠加。 其缺点是&#xff0c;…

数据结构 | 二叉树

基本形状 可参照 数据结构&#xff1a;树(Tree)【详解】_数据结构 树_UniqueUnit的博客-CSDN博客 二叉树的性质 三种顺序遍历

基于Java的演唱会网上订票系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

记录一次SQL注入src挖掘过程

记录一次SQL注入src挖掘过程&#xff0c;小白挖洞 先是使用谷歌语法找到一个可以注入的网站 谷歌语法&#xff1a; 公司inurl:php?id 然后该公司的URL为 URL:XXXXXXXXXX/xxx.php?id1 输入测试有无注入&#xff0c;有没有waf 发现有报错 使用sqlmap寻找注入点 python…

golang 结合 cobra 使用 chatgpt qdrant 实现 ai知识库 cli

golang 结合 cobra 使用 chatgpt qdrant 实现 ai知识库 cli 流程 将数据集 通过 openai embedding 得到向量组装payload,存入 qdrant用户进行问题搜索,通过 openai embedding 得到向量,从 qdrant 中搜索相似度大于0.8的数据从 qdrant 中取出数据得到参考答案将问题标题参考答案…

Flutter笔记:用于ORM的Floor框架简记

Flutter笔记 用于ORM的Floor框架简记 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/133377191 【介绍】&#xff1a;最近想找用于Dart和Flutter的ORM框架&#xff0c;偶然间发现了Floor&#xff0c;觉得还不错&#xff0c;做一些记录。 1. Floor 框…

likeadmin和fastapi的bug

以下内容写于2023年8月11日 bug 1 请求体 - 多个参数 - FastAPI (tiangolo.com)中“请求体中的单一值”处&#xff0c;选python3.6&#xff0c;接口示例代码是 from typing import Unionfrom fastapi import Body, FastAPI from pydantic import BaseModel from typing_exte…

26662-2011 磁粉制动器 阅读笔记

声明 本文是学习GB-T 26662-2011 磁粉制动器. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了磁粉制动器(以下简称制动器)的术语&#xff0c;技术要求&#xff0c;试验方法&#xff0c;检验规则&#xff0c;标志、包装、运 输和…

【项目实战】单数据源多数据库实现多租户

文章目录 前言多租户的四种实现方案单数据源多数据库实现思路代码实现 总结 前言 多租户&#xff08;Multi-Tenancy&#xff09;是一种软件架构设计模式&#xff0c;旨在使单个应用程序可以同时为多个租户&#xff08;如不同组织、用户或客户&#xff09;提供服务&#xff0c;…

Python 小爬虫入门 -- 爬取专栏文章标题保存到 CSV 文件中

爬取专栏文章标题保存到 CSV 文件中目标分析网页代码及理解代码段一代码段二成果展示爬取专栏文章标题保存到 CSV 文件中 目标 从一个网页上抓取数据,并保存到一个 CSV 文件中。 具体是爬取 微机系统与接口上机实验_TD PITE型 专栏里的所有 文章标题 并 保存到 csv 文件 中…

估计、偏差和方差

一、介绍 统计领域为我们提供了很多工具来实现机器学习目标&#xff0c;不仅可以解决训练集上的任务&#xff0c;还可以泛化。基本的概念&#xff0c;例如参数估计、偏差和方差&#xff0c;对于正式地刻画泛化、欠拟合和过拟合都非常有帮助。 二、参数估计 参数估计 是统计学…

35 LRU缓存

LRU缓存 题解1 双map&#xff08;差2个testcases&#xff09;题解2 哈希表双向链表&#xff08;参考&#xff09;题解3 STL:listunordered_map 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正…

通讯网关软件016——利用CommGate X2Access实现OPC数据转储Access

本文介绍利用CommGate X2ACCESS实现从OPC Server读取数据并转储至ACCESS数据库。CommGate X2ACCESS是宁波科安网信开发的网关软件&#xff0c;软件可以登录到网信智汇(http://wangxinzhihui.com)下载。 【案例】如下图所示&#xff0c;实现从OPC Server读取数据并转储至ACCESS…

【前段基础入门之】=>CSS 常用的字体文本属性

导读&#xff1a; 这一章&#xff0c;主要分享一些 CSS 中的一些&#xff0c;常用的 字体和文本方面的属性。 文章目录 字体属性字体大小字体族字体风格字体粗细字体复合写法 文本属性文本间距文本修饰文本缩进文本水平对齐行高vertical-align 字体属性 字体大小 属性名&…

进入Linux的世界

了解Linux的历史 一、Linux发展史二、企业应用现状三、Linux操作系统的各种版本 一、Linux发展史 了解一下硅谷模式: 1945年——1991年是美苏冷战的时间&#xff0c;在这个环境背景下&#xff0c;计算机诞生了。 Linux的发展史&#xff1a; 查看Linux纯源代码 二、企业…

No144.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…