手把手带你实现通讯录C语言

news2025/1/11 23:49:17

        通讯录大家都很熟悉了,一个联系人包括姓名,年龄,性别,电话,地址等;

        那我们想一想我们所学的数据类型(int,float,double等都是单一的相同类型 (属于内置类型))都没能很好的解决这些不同的成员 这时我们可以试试结构体(自定义类型) 结构体中可以有多个不同类型的成员 所以很符合我们的需求 那我们就定义一个联系人的个人信息

​
#define ADDER_MAX 20
#define NAME_MAX 20
#define TELE_MAX 15

typedef struct Presonal
{
	int age;
	char adder[ADDER_MAX];
	char name[NAME_MAX];
	char tele[TELE_MAX];
}contact;

​

简单了解下 typedef 这个关键词 这是对类型进行定义的 将后期可变的类型进行重新定义 以方便我们之后的操作 这个和上面的宏定义是一样的效果 都是简化操作 提高可读性 之后也会在程序中用到 先了解了解

我们通过用宏去定义数组的大小(方便我们之后对信息大小的改动) 学过数组的都知道 下标引用操作符"[ ]"里面是不能用变量的 必须是常量 亦不排除 有些版本的软件会有变长数组的概念 就是可以用变量去作为下标 做出动态内存开辟的效果 

上面的代码只是将结构体类型声明了出来 并没有实际的价值(还没用定义)

什么才叫定义呢?就是开辟空间 下面就是对结构体变量进行定义 两种方式 上面使用了typedef对结构体类型做了简化

​
struct Presonal con;
contact con;

​

这些信息需要去存储 我们可以用数组去存储这些联系人的信息 

我们之前写的数组是(int 等类型的数据类型) 那我们也可以用结构体类型进行定义数组 这样数组中每个单元就是一个结构体类型 这是我们就可以记录这些联系人的个人信息  同时我们也要知道已经存储了多少个联系人 这时就可以用一个变量去记录 

如果将他们分开写的话 对于阅读你代码的人肯定可读性会受影响 那么我们也就可以采取上边个人信息的创建方法 将结构体数组 和 联系人数量放到同一块空间中 (放结构体中)

这里有两个版本:1,静态版本

#define MAX 100
typedef struct Contact
{
	Presonal arr[MAX];
	int sz;
}Contact;

2,动态版本

​
typedef struct Contact
{
	Presonal* arr;
	int sz;
}Contact;

​

里面的第一个成员变量就是后面要开辟的 动态数组 

其实两个版本的实现就只是在数组方面 的实现 仅此而已 熟练之后 动态版本轻而易举

先看看静态版本的实现吧~

Contact.h

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


#define ADDER_MAX 20
#define NAME_MAX 20
#define TELE_MAX 15

typedef struct Presonal
{
	int age;
	char adder[ADDER_MAX];
	char name[NAME_MAX];
	char tele[TELE_MAX];
}Presonal;


#define MAX 100
typedef struct Contact
{
	Presonal arr[MAX];
	int sz;
}Contact;


//初始化
void Inti(Contact* pc);

//增加
void Add(Contact* pc);

//删除
void Del(Contact* pc);

//查找
void Search(Contact* pc);

//修改
void Modify(Contact* pc);

//展示
void Show(Contact* pc);

Contact.c

​
void Inti(Contact* pc)
{
	pc->sz = 0;
	memset(pc->arr, 0, sizeof(pc->arr));//是将结构体数组地址的每一个字节初始化为0
}

void Add(Contact* pc)
{
	//判断是否空间已满
	if (pc->sz == MAX)
	{
		printf("通讯簿已满 无法增加");
		return;//结束该函数
	}
	printf("请输入联系人的姓名 年龄 电话 地址(每个信息用空格隔开):>");
	scanf("%s %d %s %s", pc->arr[pc->sz].name, &(pc->arr[pc->sz].age), pc->arr[pc->sz].tele, pc->arr[pc->sz].adder);
	//将联系人信息数量+1
	pc->sz++;
	printf("添加成功");
}

void Del(Contact* pc)
{
	//判断电话薄是否为空 为空了就不应该删除
	if (pc->sz == 0)
	{
		printf("电话薄为空 无法删除数据");
		return;
	}
	//只需将pc->sz--
	pc->sz--;
}

//void Search(Contact* pc)
//{
//	//判断电话薄是否为空 为空了就找不了
//	if (pc->sz == 0)
//	{
//		printf("电话薄为空 无法删除数据");
//			return;
//	}
//	printf("请输入你要查找的联系人姓名>");
//	char name[NAME_MAX] = "";
//	scanf("%s", name);
//	while (strcmp(name, pc->arr[pc->sz].name) != 0)
//	{
//		printf("该联系人信息为>\n");
//		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", pc->arr[pc->sz].name, pc->arr[pc->sz].age, pc->arr[pc->sz].tele, pc->arr[pc->sz].adder);
//	}
//}


int ByNameSearch(Contact* pc)
{
	printf("请输入你要查找的联系人姓名>");
	char name[NAME_MAX] = "";
	scanf("%s", name);
	int pos = 0;
	while (strcmp(name, pc->arr[pos++].name) != 0)
	{
		if (pc->sz == pos)
		{
			printf("该联系人不存在");
			return -1;
		}
	}
	return pos - 1;//多加了一次
}

void Search(Contact* pc)
{
	//判断电话薄是否为空 为空了就找不了
	if (pc->sz == 0)
	{
		printf("电话薄为空 无法删除数据");
		return;
	}
	int pos = ByNameSearch(pc);
	if (pos == -1)
	{
		return;
	}
	printf("该联系人信息为>\n");
	printf("%-20s\t%-4d\t%-5s\t%-12s\n", pc->arr[pos].name, pc->arr[pos].age,
		pc->arr[pos].tele, pc->arr[pos].adder);
}

void Modify(Contact* pc)
{
	//判断电话薄是否为空 为空了就找不了
	if (pc->sz == 0)
	{
		printf("电话薄为空 无法删除数据");
		return;
	}
	int pos = ByNameSearch(pc);
	if (pos == -1)
		return;
	printf("输入你要修改的联系人信息 姓名 年龄 电话 地址(并用空格隔开)");
	scanf("%s %d %s %s", &pc->arr[pos].name, &pc->arr[pos].age, &pc->arr[pos].tele, &pc->arr[pos].adder);
	printf("修改成功\n");
}


void Show(Contact* pc)
{
	printf("%-20s\t%-4s\t%-5s\t%-12s\n", "名字", "年龄", "电话", "地址");
	for (int pos = 0; pos < pc->sz; pos++)
	{
		printf("%-20s\t%-4d\t%-5s\t%-12s\n", pc->arr[pos].name, pc->arr[pos].age,
			pc->arr[pos].tele, pc->arr[pos].adder);
	}
}

​

test.c

void menu()
{
	printf("***************************************\n");
	printf("******1.增                 2.删   ****\n");
	printf("******3.查                 4.改   *****\n");
	printf("******0.退出               5.展示 *****\n");
	printf("***************************************\n");
}


enum Con
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW
};


int main()
{
	int input = 0;
	Contact con;
	menu();
	Inti(&con);
	do
	{
		printf("请输入你的选择:");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			Add(&con);
			break;
		case DEL:
			Del(&con);
			break;
		case SEARCH:
			Search(&con);
			break;
		case MODIFY:
			Modify(&con);
			break;
		case SHOW:
			Show(&con);
			break;
		default:
			printf("输入错误 请重新输入");
			break;
		}
	} while (input);
	return 0;
}

test.c 项目解释 里面运用了 枚举 switch case语句 等

枚举可以增强我们代码的可读性

运行结果展示

 

之前是静态存储的通讯簿 静态的通讯簿有很多缺点

内存问题:无法有效的确定通讯簿的大小 (开多了浪费少了不够用)最明显

我们学了动态内存管理之后就可以使通讯簿自己添加申请内存 大大方便了我们存储联系人 

当然我们刚开始会向堆去申请4个字节的空间 (不大不小)如果不够我们再将容量扩大到原来的二倍 这就使我们能够有效的处理内存问题 也不是至于开多或开少 

这个二倍关系是权衡之后采取的 你也可以按照自己的想法去开辟

​
void Inti(Contact* pc)
{
	pc->sz = pc->capacity = 0;
	pc->arr = NULL;
}
void Add(Contact* pc)
{
	//判断是否空间已满
	if (pc->sz == pc->capacity)
	{
		int newcapacity = (pc->capacity == 0) ? 4 : pc->capacity * 2;
		Presonal* tmp = (Presonal*)malloc(sizeof(Presonal) * newcapacity);
		if (tmp == NULL)
		{
			printf("failed to allocate memory.");
			exit(-1);
		}
		pc->arr = tmp;
		pc->capacity = newcapacity;
	}
	printf("请输入联系人的姓名 年龄 电话 地址(每个信息用空格隔开):>");
	scanf("%s %d %s %s", pc->arr[pc->sz].name, &(pc->arr[pc->sz].age), pc->arr[pc->sz].tele, pc->arr[pc->sz].adder);
	//将联系人信息数量+1
	pc->sz++;
	printf("添加成功");
}

​

只有添加时内存会受内存影响 所以我只列出了 内存改动部分 前面给出了 动态内存对应的结构体

但是我们这个通讯簿还有问题 既然是通讯簿 就是记录你的联系人的 如果丢失就会对我们造成影响 

之前不管是静态内存管理 还是动态内存管理 都不会让我们的程序在结束之后记录和保存 我们之前存储的联系人信息  因为这些联系人是被存储在内存中的 而程序结束之后 内存会被操作系统回收

我们学了文件管理之后 就能让这些联系人信息存储在文件之中 就可以永久保存 这样在下一次存储时 只需打开之前的文件 进行相应的操作就可以了 

当然保存文件应该在通讯薄销毁之前 不然信息都销毁了还拿什么保存

文件保存代码 关于文件操作的函数还有很多 不再过多解释

void Save(Contact* pc)
{
	FILE* pf = fopen("contact.dat", "wb");
	if (pf == NULL)
	{
		perror("SaveContact::fopen");
		return;
	}
	//写数据
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		fwrite(pc->arr + i, sizeof(Presonal), 1, pf);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	printf("保存成功...\n");
}

同时 在下一次录入通讯簿时 将之前的信息内容加载进去

static Buy(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		int newcapacity = (pc->capacity == 0) ? 4 : pc->capacity * 2;
		Presonal* tmp = (Presonal*)malloc(sizeof(Presonal) * newcapacity);
		if (tmp == NULL)
		{
			printf("failed to allocate memory.");
			exit(-1);
		}
		pc->arr = tmp;
		pc->capacity = newcapacity;
	}
}

void Load(Contact* pc)
{
	//打开文件
	FILE* pf = fopen("contact.dat", "rb");
	if (pf == NULL)
	{
		perror("LoadContact::fopen");
		return;
	}
	//读文件
	Presonal tmp = { 0 };
	while (fread(&tmp, sizeof(Presonal), 1, pf))
	{
		Buy(pc);
		pc->arr[pc->sz++] = tmp;
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
}

利用 二进制读(fread 返回值size_t ) 一次读取一个(返回的是有效的读取个数)

将他放进一个临时变量中 因为要从 pf文件中放入一个临时变量里再将变量放到通讯簿中 别忘了通讯簿里面的信息个数要++ 

读取存入肯定涉及空间的扩容 所以再存入之前对空间进行检查Buy

文件中存的就是二进制的信息内容

 上面的三个文件是没涉及文件相关的代码 下面是涉及文件的相关代码

contact.c

static Buy(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		int newcapacity = (pc->capacity == 0) ? 4 : pc->capacity * 2;
		Presonal* tmp = (Presonal*)malloc(sizeof(Presonal) * newcapacity);
		if (tmp == NULL)
		{
			printf("failed to allocate memory.");
			exit(-1);
		}
		pc->arr = tmp;
		pc->capacity = newcapacity;
	}
}

void Load(Contact* pc)
{
	//打开文件
	FILE* pf = fopen("contact.dat", "rb");
	if (pf == NULL)
	{
		perror("LoadContact::fopen");
		return;
	}
	//读文件
	Presonal tmp = { 0 };
	while (fread(&tmp, sizeof(Presonal), 1, pf))
	{
		Buy(pc);
		pc->arr[pc->sz++] = tmp;
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
}

void Inti(Contact* pc)
{
	pc->sz = pc->capacity = 0;
	pc->arr = NULL;


	Load(pc);
}

void Save(Contact* pc)
{
	FILE* pf = fopen("contact.dat", "wb");
	if (pf == NULL)
	{
		perror("SaveContact::fopen");
		return;
	}
	//写数据
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		fwrite(pc->arr + i, sizeof(Presonal), 1, pf);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	printf("保存成功...\n");
}

void DestroyConact(Contact* pc)
{
	free(pc->arr);
	pc->arr = NULL;
	pc->capacity = 0;
	pc->sz = 0;
	printf("释放内存.....\n");
}

void Add(Contact* pc)
{
	Buy(pc);
	printf("请输入联系人的姓名 年龄 电话 地址(每个信息用空格隔开):>");
	scanf("%s %d %s %s", pc->arr[pc->sz].name, &(pc->arr[pc->sz].age), pc->arr[pc->sz].tele, pc->arr[pc->sz].adder);
	//将联系人信息数量+1
	pc->sz++;
	printf("添加成功");
}


void Del(Contact* pc)
{
	//判断电话薄是否为空 为空了就不应该删除
	if (pc->sz == 0)
	{
		printf("电话薄为空 无法删除数据");
		return;
	}
	//只需将pc->sz--
	pc->sz--;
}


int ByNameSearch(Contact* pc)
{
	printf("请输入你要查找的联系人姓名>");
	char name[NAME_MAX] = "";
	scanf("%s", name);
	int pos = 0;
	while (strcmp(name, pc->arr[pos++].name) != 0)
	{
		if (pc->sz == pos)
		{
			printf("该联系人不存在");
			return -1;
		}
	}
	return pos - 1;//多加了一次
}

void Search(Contact* pc)
{
	//判断电话薄是否为空 为空了就找不了
	if (pc->sz == 0)
	{
		printf("电话薄为空 无法删除数据");
		return;
	}
	int pos = ByNameSearch(pc);
	if (pos == -1)
	{
		return;
	}
	printf("该联系人信息为>\n");
	printf("%-20s\t%-4d\t%-5s\t%-12s\n", pc->arr[pos].name, pc->arr[pos].age,
		pc->arr[pos].tele, pc->arr[pos].adder);
}

void Modify(Contact* pc)
{
	//判断电话薄是否为空 为空了就找不了
	if (pc->sz == 0)
	{
		printf("电话薄为空 无法删除数据");
		return;
	}
	int pos = ByNameSearch(pc);
	if (pos == -1)
		return;
	printf("输入你要修改的联系人信息 姓名 年龄 电话 地址(并用空格隔开)");
	scanf("%s %d %s %s", &pc->arr[pos].name, &pc->arr[pos].age, &pc->arr[pos].tele, &pc->arr[pos].adder);
	printf("修改成功\n");
}


void Show(Contact* pc)
{
	printf("%-20s\t%-4s\t%-5s\t%-12s\n", "名字", "年龄", "电话", "地址");
	for (int pos = 0; pos < pc->sz; pos++)
	{
		printf("%-20s\t%-4d\t%-5s\t%-12s\n", pc->arr[pos].name, pc->arr[pos].age,
			pc->arr[pos].tele, pc->arr[pos].adder);
	}
}

contact.h

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


#define ADDER_MAX 20
#define NAME_MAX 20
#define TELE_MAX 15

typedef struct Presonal
{
	int age;
	char adder[ADDER_MAX];
	char name[NAME_MAX];
	char tele[TELE_MAX];
}Presonal;


#define MAX 100
//typedef struct Contact
//{
//	Presonal arr[MAX];
//	int sz;
//}Contact;
typedef struct Contact
{
	Presonal* arr;
	int sz;
	int capacity;
}Contact;

//初始化
void Inti(Contact* pc);

//增加
void Add(Contact* pc);

//删除
void Del(Contact* pc);

//查找
void Search(Contact* pc);

//修改
void Modify(Contact* pc);

//展示
void Show(Contact* pc);

//保存文件
void Save(Contact* pc);

//内存释放
void DestroyConact(Contact* pc);

test.c

#include"Contact.h"

void menu()
{
	printf("***************************************\n");
	printf("******1.增                 2.删   ****\n");
	printf("******3.查                 4.改   *****\n");
	printf("******0.退出               5.展示 *****\n");
	printf("***************************************\n");
}


enum Con
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW
};


int main()
{
	int input = 0;
	Contact con;
	menu();
	Inti(&con);
	do
	{
		printf("请输入你的选择:");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			Add(&con);
			break;
		case DEL:
			Del(&con);
			break;
		case SEARCH:
			Search(&con);
			break;
		case MODIFY:
			Modify(&con);
			break;
		case SHOW:
			Show(&con);
			break;
		case EXIT:
			Save(&con);
			DestroyConact(&con);
			break;
		default:
			printf("输入错误 请重新输入");
			break;
		}
	} while (input);
	return 0;
}

以上就是通讯簿的相关实现

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

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

相关文章

#详细介绍!!! 造成死锁的原因以及解决方案!

本篇主要是介绍什么是死锁&#xff0c;已经死锁产生的原因&#xff0c;如果避免死锁。根据上述的几个问题让我们来阅读本篇文章。 目录 1. 什么是死锁 2. 形成死锁的原因&#xff08;四个必要条件&#xff09; 3. 如果有效避免死锁 1. 什么是死锁 死锁主要是锁彼此间进行锁等…

FPGA实现JPEG-LS图像压缩,有损无损可配置,提供工程源码和技术支持

目录 1、前言2、JPEG-LS图像压缩理论3、JPEG-LS图像压缩性能介绍4、JPEG-LS图像压缩时序介绍5、JPEG-LS图像压缩输出压缩流6、工程源码和仿真7、福利&#xff1a;工程代码的获取 1、前言 JPEG-LS&#xff08;简称JLS&#xff09;是一种无损/有损的图像压缩算法&#xff0c;其无…

1 Go语言开发环境搭建详细教程【Go语言教程】

Go语言开发环境搭建【Win、Linux、Mac】 1 SDK下载 官网地址&#xff1a;golang.org,因为一些原因国内可能无法访问。可以使用下面第二个链接。国内地址访问&#xff1a;https://golang.google.cn/dl或者https://www.golangtc.com/download 根据自己操作系统版本&#xff0c;下…

30天学会《Streamlit》(9)

30天学会《Streamlit》是一项编码挑战&#xff0c;旨在帮助您开始构建Streamlit应用程序。特别是&#xff0c;您将能够&#xff1a; 为构建Streamlit应用程序设置编码环境 构建您的第一个Streamlit应用程序 了解用于Streamlit应用程序的所有很棒的输入/输出小部件 第9天 - …

超高精度PID控制器的特殊功能(4)——分程控制功能及其应用

摘要&#xff1a;分程控制作为一种典型的复杂控制方法之一&#xff0c;常用于聚合反应工艺、冷热循环浴、TEC半导体温度控制、动态平衡法的真空和压力控制等领域。为快速和便捷的使用分程控制&#xff0c;避免采用PLC时存在的控制精度差和使用门槛高等问题&#xff0c;本文介绍…

pyqt6+vtk

这里用PyQt6vtk9.2.6 pip install PyQt6 pyqt6-tools vtk这里拉了一个水平布局 然后水平布局中加入QWidget&#xff0c;object name改为vtkWidget 右键Promote Widgets Promoted class name: QVTKRenderWindowInteractor Header file: vtkmodules.qt.QVTKRenderWindowInterac…

文章改写神器在线-AI续写文章生成器

AI续写生成器 AI续写生成器是一种利用人工智能技术的创意工具&#xff0c;能够提高写作效率&#xff0c;为营销推广带来全新的可能性。无论你是写手、广告人员还是市场营销人员&#xff0c;这个工具都能够有效地解决你在写作中遇到的难题。 在内容创作行业中&#xff0c;原创…

Amazon Linux2部署安装Jenkins

先决条件 服务器配置要求 256 MB of RAM 1 GB of drive space (although 10 GB is a recommended minimum if running Jenkins as a Docker container) 需要部署安装JDK环境部署安装的Jenkins版本为Version 2.400 部署安装JDK 1. 下载JDK软件包 wget https://corretto.aws/…

Altium Designer借助嘉立创添加PCB封装和3D模型

目录 引言打开立创专业版EDA&#xff0c;建立项目从立创商城找到器件编码添加PCB封装导出和修改3D封装 引言 由于使用Altium Designer的频率并不是特别高&#xff0c;所以每一次使用总是得东跌西撞的才回忆起一些使用步骤。因此&#xff0c;想在这里记录一下Altium Designer借…

【mysql】binlog日志

目录 1.1 基本说明1.2 binlog日志格式1.3 binlog日志查看1.4 binlog日志删除1.5 binlog操作示例 1.1 基本说明 1.全称binary log&#xff0c;二进制日志 2.记录了所有的DDL语句&#xff08;Data Definition Language数据定义语言&#xff09;和DML语句&#xff08;Data Manipul…

Java多线程中sleep()方法和wait()方法的区别

目录 具体而言 &#xff0c;sleep&#xff08;&#xff09;方法与wait&#xff08;&#xff09;方法的区别主要表现在以下几个方面&#xff1a; 引申&#xff1a;sleep&#xff08;&#xff09;方法和yield&#xff08;&#xff09;方法有什么区别&#xff1f; 常见面试题&a…

如何衡量算法的效率?时间复杂度空间复杂度

本篇博客会讲解如何衡量一个算法的效率。衡量算法的效率&#xff0c;主要有2个维度&#xff0c;分别是&#xff1a;时间复杂度和空间复杂度。 时间复杂度用来衡量算法的时间效率。时间复杂度越低&#xff0c;算法的耗时越短&#xff0c;效率则越高。空间复杂度用来衡量算法的空…

Space Cloud updated Crack

Space Cloud updated Crack Space Git Flow-对于实践连续发布周期的开发团队来说&#xff0c;在协调团队成员、建立流程和集成多个工具时&#xff0c;建立交付流可能需要付出巨大努力。为了更容易做到这一点&#xff0c;引入了JetBrains Space Git流&#xff0c;这是一个完整的…

同一热卖商品的高并发写难题 - Lua脚本扣减库存方案

目录 一、Mysql高并发写时的行锁难题 二、Redis的相关命令 1. WATCH命令 2. EVAL与EVALSHA命令 三、SpringBoot执行Lua脚本代码示例 1. 依赖包 2. Lua脚本sku.lua 3. 加载Lua脚本 4. 执行Lua脚本 四、参考资料 一、Mysql高并发写时的行锁难题 通常来说&#xff0c;秒…

百度大模型ERNIE3.0

大模型对比 文心全景图 ERNIE 3.0

《Java8实战》第9章 重构、测试和调试

9.1 为改善可读性和灵活性重构代码 Lambda 表达式可以帮助我们用更紧凑的方式描述程序的行为。 9.1.1 改善代码的可读性 可读性非常主观&#xff0c;但是通俗的理解就是“别人理解这段代码的难易程度”。改善可读性意味着你要确保你的代码能非常容易地被包括自己在内的所有…

【Java|基础篇】方法的定义使用、重载以及递归

文章目录 1.什么是方法2.方法的定义和使用返回值类型形参与实参方法执行过程 3.⭐方法的重载4.递归5. 总结 1.什么是方法 方法就是组织好的,可重复使用的具有某种功能的特定代码块 (类似于函数) 在我们平时写代码时,如果遇到会经常使用的一些功能相同的代码时,我们就可以把这段…

在线考试系统学员答题批改日志,实战练习

一、环境要求 sandbox-hdp 2.6.4 或同等版本自建的HadoopHiveSparkHBase 开发环境。 二、数据描述 这是一份来自于某在线考试系统的学员答题批改日志&#xff0c;日志中记录了日志生成时间,题目难度系数&#xff0c;题目所属的知识点 ID&#xff0c;做题的学生 ID&#xff0…

Oracle的学习心得和知识总结(二十一)|Oracle数据库可插拔数据库PDB的创建及删除

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《Oracle Database SQL Language Reference》 2、参考书籍&#xff1a;《PostgreSQL中文手册》 3、EDB Postgres Advanced Server User Gui…