【C语言】——通讯录(静态-动态增长-文件储存)

 

目录

前言:

一:整体框架

关于通讯录结构体的创建 

二:通讯录的功能实现(静态)

2.1初始化通讯录

2.2增加联系人

2.3打印通讯录

2.4删除联系人

 2.5 查找联系人

2.6修改联系人 

2.7排序联系人

三:通讯录优化——动态内存

 3.1通讯录的创建

3.2初始化通讯录 

3.3增加联系人 

3.4清空通讯录 

 四:通讯录优化——文件版本

4.1退出保存信息到文件

 4.2初始化时加载文件信息

五:整体代码

test.c

 contact.c

contact.h

前言:

在之前的篇章中讲述了【C语言】进阶——结构体,【C语言】进阶——动态内存,【C语言】进阶——文件操作。

在本篇运用以上知识结合来写一个小项目——通讯录

我会逐步从静态版本优化到动态增加以及最终的文件存储版,循循渐进,详解通讯录的实现

实现思路

通讯录类似一个复杂结构体,包含了很多信息,以个人信息的通讯录而言,需要包含个人名字,年龄,电话,性别以及地址;

而它所具有的基本功能:增(Add)删(Del) 改(modify) 查 (Search);以及我们可以对其一些简单的拓展功能;

这些具体分析为:

  1. 打印一个菜单,提供用户选择功能;
  2. 添加联系人信息;
  3. 删除联系人信息;
  4. 查询联系人信息;
  5. 修改联系人信息;
  6. 显示所有联系人信息;
  7. 对所有联系人信息进行排序整理;
  8. 删除所有联系人信息;
  9. 操作完毕可选择退出。

将整个项目分为三部分 :

test.c ——测试代码模块

contact.c——函数的实现

contact.h——类型的定义,函数声明等 

一:整体框架

我们需要映入眼帘的菜单选择和提示,可以选择do...while();和switch语句相结合,来打印出整个框架体系;

另外因为switch语句中case 1,case2...这样的数字不好看,我们可以采用枚举变量来定义;

        枚举的关键字是enum

在括号内部注意每个成员名后面要加逗号,同时别忘了括号外面的分号

枚举内部的成员被依次赋值为0,1,2

我们假如将female赋值为1,secret就被赋值为2,male赋值为0

        枚举的优点:

1.增加代码的可读性和可维护性

2. 和 #define 定义的标识符比较枚举有类型检查,更加严谨。

3. 防止了命名污染(封装)

4. 便于调试

5. 使用方便,一次可以定义多个常量

void menu()
{printf("<-==============通讯录=============->\n");printf("<-==========1.增加联系人===========->\n");printf("<-==========2.删除联系人===========->\n");printf("<-==========3.查找联系人===========->\n");printf("<-==========4.修改联系人===========->\n");printf("<-==========5.打印通讯录===========->\n");printf("<-==========6.排序通讯录===========->\n");printf("<-==========0.退出通讯录===========->\n");printf("<-=================================->\n");
}
enum Option
{EXIT,	//0ADD,DEL,SEARCH,MODIFY,SHOW,SORT
};
int main()
{int input = 0;do{//菜单menu();printf("请输入操作:");scanf("%d", &input);switch (input){case ADD:printf("增加联系人\n");break;case DEL:printf("删除联系人\n");break;case SEARCH:printf("查找联系人\n");break;case MODIFY:printf("修改联系人\n");break;case SHOW:printf("打印联系人\n");break;case SORT:printf("排序联系人\n");break;case EXIT:printf("退出通讯录\n");break;default:printf("输入错误,重新输入\n");break;}} while (input);return 0;
}

 

关于通讯录结构体的创建 

创建所需的个人信息结构体

利用#define 定义常量,有利于维护;

#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30typedef struct Peo		//个人信息结构体
{char name[NAME_MAX];int age;char sex[SEX_MAX];char tele[TELE_MAX];char addr[ADDR_MAX];
}Peo;

另外还需要创建一个结构体,来记录Peo的信息,以数组来存放,创建变量,记录个数 

typedef struct Contact
{Peo data[DATA_MAX];		//存放个人信息int sz;		//记录信息人数
}Contact;

二:通讯录的功能实现(静态)

2.1初始化通讯录

//静态-初始化通讯录
void InitContact(Contact* pc)
{assert(pc);pc->sz = 0;memset(pc->data, 0, sizeof(pc->data)); 
}

memset来初始化,以字节为单位一个字节一个字节的初始化! 

2.2增加联系人

增加之前,先判断是否为满,增加成功后,还需要将sz++,记录加入的人数

//增加联系人
void AddContact(Contact* pc)
{assert(pc);//判断通讯录是否满了if (pc->sz == DATA_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);//对sz增加,pc->sz++;printf("增加成功\n");
}

 

2.3打印通讯录

增加联系人后,真的在内存中增加与否,并不知道,我们需要打印出来

先判断是否有信息,然后打印标题,增加可读性,再利用for循环,将sz个信息打印出来 

-20就代表域宽是20(长度不够20用空格填充),负号代表左对齐,默认是右对齐的!

//显示所有的联系人
void ShowContact(const Contact* pc)
{assert(pc);//判断通讯录内是否有信息if (pc->sz == 0){printf("打印失败,通讯录为空\n");return;}//打印//打印标题printf("%-20s%-5s%-5s%-12s%-30s\n", "名字", "年龄", "性别", "电话", "地址");//打印信息int i = 0;for (i = 0; i < pc->sz; i++){//打印每个人的信息printf("%-20s%-5d%-5s%-12s%-30s\n",pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);}
}

2.4删除联系人

  1. 先判断通讯录是否有信息,通过名字来查找是否存在此人,
  2. 封装函数:因为我们在查找联系人,修改联系人中也会用到此功能。查找到此人,返回sz位置,没有此人,返回-1;
  3.  通过for循环:假如要删除人为 tmp的信息;就只需要把tmp后的数据往前移动,覆盖掉tmp位置的信息就可以了;
  4. 如果要删除最后一个人,则直接sz--;可以不进行访问,算是删除了
  5. 删除成功后,就把sz--;说明数组里的有效数据减1!
//通过名字查找被删除人
static int FindByName(Contact* pc, char* name)
{assert(pc);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)
{assert(pc);//判断是否为空if (pc->sz == 0){printf("删除失败,通讯录为空\n");return;}//查找是否存在此人char name[NAME_MAX];printf("请输入被删除人名字:\n");scanf("%s", name);//查找此人,存在返回位置,不存在返回-1int ret = FindByName(pc,name);if (ret == -1){printf("要删除的人不存在\n");return;}//删除(覆盖)此人for (int i = ret; i < pc->sz - 1; i++)	//sz-1 是防止执行体越界,如果要删除的是最后一个元素,通过sz--,直接不访问,{pc->data[i] = pc->data[i + 1];}pc->sz --;printf("删除成功\n");
}

 

 

 2.5 查找联系人

跟删除同理,先查找是否存在此人,查找到了返回下标打印出来即可 

//查找联系人
void FindContact(Contact* pc)
{assert(pc);//判断是否为空if (pc->sz == 0){printf("查找失败,通讯录为空\n");return;}//查找此人,存在返回位置,不存在返回-1char name[NAME_MAX];printf("请输入被查找人名字:\n");scanf("%s", name);int ret = FindByName(pc, name);if (ret == -1){printf("要查找的人不存在\n");return;}//显示出来printf("%-20s%-5s%-5s%-12s%-30s\n", "名字", "年龄", "性别", "电话", "地址");printf("%-20s%-5d%-5s%-12s%-30s\n",pc->data[ret].name, pc->data[ret].age, pc->data[ret].sex, pc->data[ret].tele, pc->data[ret].addr);
}

2.6修改联系人 

查找到此人位置,记录返回,通过该位置进行修改 

//修改联系人
void ModifyContact(Contact* pc)
{assert(pc);//判断是否为空if (pc->sz == 0){printf("修改失败,通讯录为空\n");return;}//查找此人,存在返回位置,不存在返回-1char name[NAME_MAX];printf("请输入被修改人名字:\n");scanf("%s", name);int ret = FindByName(pc, name);if (ret == -1){printf("要查找的人不存在\n");return;}//修改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");
}

 

2.7排序联系人

利用qsort快速排序,然后我们以名字strcmp函数来比较

//目录排序
//void qsort(void* base, size_t num, size_t size,
//	int (*compar)(const void*, const void*));
int cmpare(const void* p1, const void* p2)
{return strcmp(((Peo*)p1)->name, ((Peo*)p2)->name);
}void SortContact(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(Peo), cmpare);
}

三:通讯录优化——动态内存

 3.1通讯录的创建

增加变量capacity——用来记录通讯录容量

利用指针动态创建内存,结合malloc,realloc,calloc

typedef struct Contact
{Peo* data;    //存放数据int sz;		    //记录信息人数int capacity;    //记录的是通讯录的当前容量
}Contact;

3.2初始化通讯录 

利用calloc 开辟动态空间给data 

#define DEFAULT_SZ 3		//默认容量
#define DEFAULT_INC 2		//扩容量
//动态版本的初始化
void InitContact(Contact* pc)
{assert(pc);pc->sz = 0;pc->capacity = DEFAULT_SZ;	pc->data = calloc(pc->capacity, sizeof(Peo));	if (pc->data == NULL){perror("InitContact->calloc");return;}
}

3.3增加联系人 

 利用realloc来进行动态扩容,存放到临时变量ptr里,开辟成功给data

//检查是否需要扩容
void CheckCapacity(Contact* pc)
{if (pc->sz == pc->capacity){Peo* ptr = (Peo*)realloc(pc->data, (pc->capacity + DEFAULT_INC) * sizeof(Peo));if (ptr != NULL){pc->data = ptr;pc->capacity += DEFAULT_INC;printf("增容成功\n");}else{perror("AddContact->realloc");return;}}
}
//增加联系人
void AddContact(Contact* pc)
{assert(pc);//增加容量CheckCapacity(pc);//增加信息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.4清空通讯录 

因为涉及到了动态开辟,所以使用后要进行free空间

防止内存泄漏

写到EXIT退出通讯录里面,让它退出直接调用这个清空销毁函数!就算不销毁最终程序结束也会自动销毁!

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

 四:通讯录优化——文件版本

4.1退出保存信息到文件

使用文件操作,涉及到了文件缓冲区;

ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序 中每一个正在使用的文件开辟一块“文件缓冲区”。

① 从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。

② 从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓 冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。

③ 缓冲区的大小根据C编译系统决定的。

因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。 如果不做,可能导致读写文件的问题。

 

先把信息存储起来后,在执行销毁并退出! 

//保存信息到文件
void SaveContact(Contact* pc)
{FILE* pf = fopen("contact.txt", "wb");	//二进制读文件if (pf == NULL){perror("SaveContact");return;}//写信息到文件int i = 0;for (i = 0; i < pc->sz; i++){//fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pf);fwrite(pc->data + i, sizeof(Peo), 1, pf);}fclose(pf);pf = NULL;
}

 4.2初始化时加载文件信息

将信息加载到文件当中,

在初始化阶段加载文件信息

void CheckCapacity(Contact* pc);//检查上次保存的信息是否需要扩容
void LoadContact(Contact* pc)
{FILE* pf = fopen("contact.txt", "rb");    //以二进制读文件if (pf == NULL){perror("LoadContact");return;}//读文件Peo tmp = { 0 };    //临时变量while (fread(&tmp, sizeof(Peo), 1, pf)){CheckCapacity(pc);pc->data[pc->sz] = tmp;pc->sz++;}fclose(pf);pf = NULL;
}//文件版本的初始化函数
void InitContact(Contact* pc)
{assert(pc);pc->sz = 0;pc->capacity = DEFAULT_SZ;pc->data = calloc(pc->capacity, sizeof(Peo));if (pc->data == NULL){perror("InitContact->calloc");return;}//加载文件中的信息到通讯录LoadContact(pc);
}

 

五:整体代码

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include "contact.h"
void menu()
{printf("<-==============通讯录=============->\n");printf("<-==========1.增加联系人===========->\n");printf("<-==========2.删除联系人===========->\n");printf("<-==========3.查找联系人===========->\n");printf("<-==========4.修改联系人===========->\n");printf("<-==========5.打印通讯录===========->\n");printf("<-==========6.排序通讯录===========->\n");printf("<-==========0.退出通讯录===========->\n");printf("<-=================================->\n");
}
enum Option
{EXIT,	//0ADD,DEL,SEARCH,MODIFY,SHOW,SORT
};
int main()
{int input = 0;Contact con;	//创建通讯录//初始化通讯录InitContact(&con);do{//菜单menu();printf("请输入操作:");scanf("%d", &input);switch (input){case ADD:printf("增加联系人\n");AddContact(&con);break;case DEL:printf("删除联系人\n");DelContact(&con);break;case SEARCH:printf("查找联系人\n");FindContact(&con);break;case MODIFY:printf("修改联系人\n");ModifyContact(&con);break;case SHOW:printf("打印联系人\n");ShowContact(&con);break;case SORT:printf("排序联系人\n");SortContact(&con);break;case EXIT:printf("退出通讯录\n");SaveContact(&con);DestroyContact(&con);break;default:printf("输入错误,重新输入\n");break;}} while (input);return 0;
}

 contact.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"静态-初始化通讯录
//void InitContact(Contact* pc)
//{
//	assert(pc);
//	pc->sz = 0;
//	memset(pc->data, 0, sizeof(pc->data)); 
//}
动态版本的初始化
//void InitContact(Contact* pc)
//{
//	assert(pc);
//	pc->sz = 0;
//	pc->capacity = DEFAULT_SZ;	
//	pc->data = calloc(pc->capacity, sizeof(Peo));	
//	if (pc->data == NULL)
//	{
//		perror("InitContact->calloc");
//		return;
//	}
//}
void CheckCapacity(Contact* pc);//检查上次保存的信息是否需要扩容
void LoadContact(Contact* pc)
{FILE* pf = fopen("contact.txt", "rb");if (pf == NULL){perror("LoadContact");return;}//读文件Peo tmp = { 0 };while (fread(&tmp, sizeof(Peo), 1, pf)){CheckCapacity(pc);pc->data[pc->sz] = tmp;pc->sz++;}fclose(pf);pf = NULL;
}//文件版本的初始化函数
void InitContact(Contact* pc)
{assert(pc);pc->sz = 0;pc->capacity = DEFAULT_SZ;pc->data = calloc(pc->capacity, sizeof(Peo));if (pc->data == NULL){perror("InitContact->calloc");return;}//加载文件中的信息到通讯录LoadContact(pc);
}
//
静态增加联系人
//void AddContact(Contact* pc)
//{
//	assert(pc);
//	//判断通讯录是否满了
//	if (pc->sz == DATA_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);
//	//对sz增加,
//	pc->sz++;
//	printf("增加成功\n");
//}
void DestroyContact(Contact* pc)
{free(pc->data);pc->data = NULL;pc->sz = 0;pc->capacity = 0;
}
//检查是否需要扩容
void CheckCapacity(Contact* pc)
{if (pc->sz == pc->capacity){Peo* ptr = (Peo*)realloc(pc->data, (pc->capacity + DEFAULT_INC) * sizeof(Peo));if (ptr != NULL){pc->data = ptr;pc->capacity += DEFAULT_INC;printf("增容成功\n");}else{perror("AddContact->realloc");return;}}
}
//增加联系人
void AddContact(Contact* pc)
{assert(pc);//增加容量CheckCapacity(pc);//增加信息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);//判断通讯录内是否有信息if (pc->sz == 0){printf("打印失败,通讯录为空\n");return;}//打印//打印标题printf("%-20s%-5s%-5s%-12s%-30s\n", "名字", "年龄", "性别", "电话", "地址");//打印信息int i = 0;for (i = 0; i < pc->sz; i++){//打印每个人的信息printf("%-20s%-5d%-5s%-12s%-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(Contact* pc, char* name)
{assert(pc);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)
{assert(pc);//判断是否为空if (pc->sz == 0){printf("删除失败,通讯录为空\n");return;}//查找是否存在此人char name[NAME_MAX];printf("请输入被删除人名字:\n");scanf("%s", name);//查找此人,存在返回位置,不存在返回-1int ret = FindByName(pc,name);if (ret == -1){printf("要删除的人不存在\n");return;}//删除(覆盖)此人for (int i = ret; i < pc->sz - 1; i++)	//sz-1 是防止执行体越界,如果要删除的是最后一个元素,通过sz--,直接不访问,{pc->data[i] = pc->data[i + 1];}pc->sz--;printf("删除成功\n");
}//查找联系人
void FindContact(Contact* pc)
{assert(pc);//判断是否为空if (pc->sz == 0){printf("查找失败,通讯录为空\n");return;}//查找此人,存在返回位置,不存在返回-1char name[NAME_MAX];printf("请输入被查找人名字:\n");scanf("%s", name);int ret = FindByName(pc, name);if (ret == -1){printf("要查找的人不存在\n");return;}//显示出来printf("%-20s%-5s%-5s%-12s%-30s\n", "名字", "年龄", "性别", "电话", "地址");printf("%-20s%-5d%-5s%-12s%-30s\n",pc->data[ret].name, pc->data[ret].age, pc->data[ret].sex, pc->data[ret].tele, pc->data[ret].addr);
}//修改联系人
void ModifyContact(Contact* pc)
{assert(pc);//判断是否为空if (pc->sz == 0){printf("修改失败,通讯录为空\n");return;}//查找此人,存在返回位置,不存在返回-1char name[NAME_MAX];printf("请输入被修改人名字:\n");scanf("%s", name);int ret = FindByName(pc, name);if (ret == -1){printf("要查找的人不存在\n");return;}//修改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 qsort(void* base, size_t num, size_t size,
//	int (*compar)(const void*, const void*));
int cmpare(const void* p1, const void* p2)
{return strcmp(((Peo*)p1)->name, ((Peo*)p2)->name);
}void SortContact(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(Peo), cmpare);
}
//保存信息到文件
void SaveContact(Contact* pc)
{FILE* pf = fopen("contact.txt", "wb");	//二进制读文件if (pf == NULL){perror("SaveContact");return;}//写信息到文件int i = 0;for (i = 0; i < pc->sz; i++){//fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pf);fwrite(pc->data + i, sizeof(Peo), 1, pf);}fclose(pf);pf = NULL;
}

contact.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<string.h>
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30#define DATA_MAX 100#define DEFAULT_SZ 3		//默认容量
#define DEFAULT_INC 2		//扩容量typedef struct Peo		//个人信息结构体
{char name[NAME_MAX];int age;char sex[SEX_MAX];char tele[TELE_MAX];char addr[ADDR_MAX];
}Peo;
typedef struct Contact
{Peo* data;//存放数据int sz;		//记录信息人数int capacity;//记录的是通讯录的当前容量
}Contact;//初始化通讯录
//void InitContact(Contact* pc);//动态版本的初始化
void InitContact(Contact* pc);//增加联系人
void AddContact(Contact* pc);//显示所有的联系人
void ShowContact(const Contact* pc);//删除联系人
void DelContact(Contact* pc);//查找联系人
void FindContact(Contact* pc);//修改联系人
void ModifyContact(Contact* pc);//排序联系人
void SortContact(Contact* pc);//销毁通讯录
void DestroyContact(Contact* pc);//保存信息到文件
void SaveContact(Contact* pc);//加载文件信息到通讯录
void LoadContact(Contact* pc);

以上就是利用三章知识点结合做的小项目——通讯录;

文中不足之处还望指点,感激不尽

 

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

14.10 Socket 套接字选择通信

对于网络通信中的服务端来说&#xff0c;显然不可能是一对一的&#xff0c;我们所希望的是服务端启用一份则可以选择性的与特定一个客户端通信&#xff0c;而当不需要与客户端通信时&#xff0c;则只需要将该套接字挂到链表中存储并等待后续操作&#xff0c;套接字服务端通过多…

Spring Boot 3.0 已经就绪,您准备好了么?

Java 微服务开发框架王者 Spring 2014 年的 4 月&#xff0c;Spring Boot 1.0.0 正式发布。距离 1.0 版本的发布已经过去了 9 年多的时间&#xff0c;如今 Spring Boot 已经被 Java 开发者广泛使用&#xff0c;正如 JRebel 的 2022 年开发者生产力报告中提到的那样&#xff0c…

pdf压缩文件怎么压缩最小?

pdf压缩文件怎么压缩最小&#xff1f;我们很多项目介绍或是学术的报告都是采用的这个pdf格式&#xff0c;那么我们在存储或是需要进行分享的时候&#xff0c;可能就会因为文件过大而导致无法打开或是发送了。那么就需要将其进行压缩。PDF文件压缩方法很多&#xff0c;pdf压缩文…

基于安卓Android的掌上酒店预订APP

项目介绍 网络的广泛应用给生活带来了十分的便利。所以把掌上酒店预订与现在网络相结合&#xff0c;利用java技术建设掌上酒店预订APP&#xff0c;实现掌上酒店预订的信息化。则对于进一步提高掌上酒店预订发展&#xff0c;丰富掌上酒店预订经验能起到不少的促进作用。 掌上酒…

Netty 入门 — 要想掌握 Netty,你必须知道它的这些核心组件

在上篇文章&#xff08;Netty 入门 — 亘古不变的Hello World&#xff09;中&#xff0c;我们简单认识了开发一个 Netty 服务端和客户端代码的主要步骤了&#xff0c;在这几大步骤中我们基本上可以看出 Netty 的几个核心组件。 在真正进入 Netty 的学习之前&#xff0c;我们非…

docker-compose安装

如果提示docker-compose未找到命令&#xff0c;需要安装一下 curl -L https://github.com/docker/compose/releases/download/1.23.0-rc2/run.sh > /usr/local/bin/docker-compose 修改权限 chmod x /usr/local/bin/docker-compose 建立软连接 ln -s /usr/local/bin/do…

如何利用BIGEMAP软件查看历史影像

工具 Bigemap gis office地图软件 BIGEMAP GIS Office-全能版 Bigemap APP_卫星地图APP_高清卫星地图APP 很多人都在寻找历史影像图&#xff0c;这块的需求是非常大&#xff0c;历史影像一般可以用于历史地貌的变迁分析&#xff0c;还原以前的生态场景&#xff0c;对范围面积…

微信小程序获取手机号和openid

小程序通过wx.login组件会返回一个code&#xff0c;这个code用来获得用户的openid。 小程序写法为&#xff1a; wx.login({success (res) {if (res.code) {//发起网络请求wx.request({url: https://example.com/onLogin,// 后台给的请求地址data: {code: res.code}})} else {…

2023年中国熔盐储能装机量、新增装机量及行业投资规模分析[图]

熔盐储能是一种可以传递能量、长时间&#xff08;6-8h&#xff09;、大容量储能的技术路径&#xff0c;作为传热介质可以实现太阳能到热能的转换&#xff0c;作为储能介质可以实现将热能和电能的双向转换&#xff0c;可以很好的适应和解决以上两大矛盾。因此&#xff0c;熔盐储…

Linux上Docker的安装以及作为非运维人员应当掌握哪些Docker命令

目录 前言 1、安装步骤 2、理解镜像和容器究竟是什么意思 2.1、为什么我们要知道什么是镜像&#xff0c;什么是容器&#xff1f; 2.2、什么是镜像&#xff1f; 2.3、什么是容器&#xff1f; 2.4、Docker在做什么&#xff1f; 2.5、什么是镜像仓库&#xff1f; 2、Dock…

Mobpush智能化精准推送,助力求职者快人一步

近日&#xff0c;“BOSS”直聘崩了的消息又又又上了热搜&#xff0c;2023年9月15日上午&#xff0c;BOSS直聘在线统计超过4700万人。由此可见&#xff0c;随着金九银十招聘旺季的到来&#xff0c;求职软件成为人们的青睐。但是对于大多数使用招聘软件的用户而言&#xff0c;往往…

2023_Spark_实验十六:编写LoggerLevel方法及getLocalSparkSession方法

一、搭建Spark项目结构 在SparkProject模块的pom.xml文件中增加一下依赖&#xff0c;并等待依赖包下载完毕&#xff0c;如上图。 ​<!-- Spark及Scala的版本号 --><properties><scala.version>2.11</scala.version><spark.version>2.1.1</sp…