哈希表实现通讯录的设计与实现

【实验目的】

为某个单位建立一个员工通讯录管理系统,可以方便查询每一个员工的电话与地址。设计哈希表存储,设计并实现通讯录查找系统。

【实验要求】

(1)每个员工记录有下列数据项:电话号码、用户名、地址,可以适当再增加数据项;员工数据保存在employee.txt文件里。
(2)从employee.txt文件读取数据,分别以用户名、电话号码为关键字建立哈希表;
(3)哈希函数采用除留余数法,解决冲突采用二次探测再散列法;
(4)对通讯录进行添加、删除、修改、浏览、查找等操作,每进行一项操作后将内存中的数据写入到文件中。
(5)记录操作者的操作内容和当地时间并记录到当前文件夹下的Log.dat文件中(为方便查看,博主用的Log.txt文件)。

【主要功能】

(1)从文件employee.txt输入数据,初始化
(2)添加一个员工数据
(3)查找员工
(4)按姓名或电话号码定位员工,返回数据的地址
(5)删除一个员工
(6)保存内存中的数据到文件中
(7)按姓名查找员工
(8)按电话号码查找员工
(9)显示所有员工的数据
(10)显示指定员工数据
(11)更改员工数据
(12)更改员工的指定数据
(13)显示原员工信息和新员工信息,判断是否修改
(14)菜单显示
(15)获得系统时间,记录日志时使用
(16)将操作日志保存到文件Log.dat中

【参考程序】

header.h

/*****************************************************************
* 版权所有(C)2022 浅笑醉红楼
* 
* 文件名称:header.h
* 内容摘要:哈希表的基本操作
* 其他说明:无
* 当前版本:1.0
* 作者:浅笑醉红楼
****************************************************************/

/*------------------头文件引用------------------*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/*------------------头文件引用------------------*/

/**************************相关宏定义**************************/
#define Hashsize 50/*哈希表长*/
#define DELEFLAG -1/*删除标志*/
/**************************相关宏定义**************************/

/************************相关结构体定义************************/
typedef struct{
	
	char add[50];/*员工地址*/
	long int num;/*员工电话号码*/
	char name[10];/*员工姓名*/
	int key;/*关键字*/

} Datatype;

typedef struct{
	Datatype data;/*数据域*/
	int times;/*比较次数*/
}HashTable[Hashsize];
/************************相关结构体定义************************/

/*----------------------------------------函数的声明----------------------------------------*/
void CreatHash(HashTable ht, Datatype items[], int n);	/*创建Hash表*/
int HashInsert(HashTable ht, Datatype x);				/*哈希表插入*/
int HashDelete(HashTable ht, Datatype x);				/*哈希表删除*/
int HashSearch(HashTable ht, Datatype x);				/*哈希表查找*/
int Collision(int d);									/*线性探测处理冲突*/
int Collision2(int d, int i);							/*平方探测处理冲突*/
int HashFunc(int num);									/*除留余数法构造哈希函数*/
int Getkey(Datatype x);									/*直接定址法获取名字的哈希地址*/
int Getkeyn(Datatype x);								/*直接定址法获取电话号码的哈希地址*/
/*----------------------------------------函数的声明----------------------------------------*/


/**
  * @brief  创建哈希表
  * @param  ht:哈希表 items:关键字序列 n:关键字个数
  * @retval 无
  */
void CreatHash(HashTable ht, Datatype items[], int n)		/*创建哈希表*/
{
	int i;

	for(i = 0; i < Hashsize; i++)/*初始化哈希表*/
	{
		ht[i].data.key = 0;
		ht[i].times = 0;
	}
	for(i  = 0; i < n; i++)
	{
		HashInsert(ht, items[i]);/*依次向哈希表中插入元素*/
	} 
}

/**
  * @brief  哈希表插入
  * @param  ht:哈希表 x:插入数据元素
  * @retval 插入成功,返回1
  */
int HashInsert(HashTable ht, Datatype x)			/*哈希表插入*/
{
	int addr;
	addr = HashSearch(ht, x);/*在哈希表中查找*/
	if(addr > 0)/*找到,不必插入*/
	{
		return 0;
	}
	ht[-addr].data = x;/*没有找到,则插入*//*-addr是因为上面返回的是-addr,故这儿是-addr(哈希地址不能为负)*/
	ht[-addr].times = 1;
	ht[-addr].data.key = 1;
	return 1;
}

/**
  * @brief  哈希表的删除
  * @param  ht:哈希表 x:删除数据元素
  * @retval 成功返回1,失败返回0
  */
int HashDelete(HashTable ht, Datatype x)/*哈希表的删除*/
{
	int addr;

	addr = HashSearch(ht, x);/*查找数据元素*/
	if(addr >= 0)/*找到,打上删除标记*/
	{
		ht[addr].data.key = -1;/*删除*/
		return 1;/*删除成功返回1*/
	}
	return 0;/*删除失败返回0*/
}

/**
  * @brief  哈希表的查找
  * @param  ht:哈希表 x:查找元素
  * @retval 查找成功返回addr,失败返回-addr
  */
int HashSearch(HashTable ht, Datatype x)/*哈希表的查找*/
{
	int addr;
	x.key = Getkey(x);/*获取键值*/
	addr = HashFunc(x.key);/*获得哈希地址*/
	x.key = 1;
	while(ht[addr].data.key != 0 && ht[addr].data.key != x.key)/*地址不为空或循环回到原点未找到查找元素,则冲突*/
	{
		addr = Collision(addr);/*没找到,处理冲突*/
	}
	if(ht[addr].data.key == x.key)
	{
		return addr;/*查找成功*/
	}
	else
	{
		return -addr;/*查找失败*/
	}
}

/**
  * @brief  线性探测处理冲突
  * @param  d:探查位置
  * @retval 返回空闲的位置
  */
int Collision(int d)/*线性探测处理冲突*/
{
	return(d + 1) % Hashsize;/*返回哈希地址*/
}

/**
  * @brief  平方探测处理冲突
  * @param  d:探查位置 i:探测次数
  * @retval 返回空闲的位置
  */
int Collision2(int d, int i)/*平方探测处理冲突*/
{
	return (int)(d + pow(-1, i - 1) * i * i) % Hashsize;/*返回哈希地址*/
}

/**
  * @brief  除留余数法构造哈希函数
  * @param  key:关键字
  * @retval 返回余数为散列地址
  */
int HashFunc(int key)/*除留余数法构造哈希函数*/
{
	return key % Hashsize;/*模为50取余*/
}

/**
  * @brief  直接定址法获取名字的哈希地址
  * @param  x:哈希地址
  * @retval 返回名字哈希地址
  */
int Getkey(Datatype x)/*直接定址法获取名字的哈希地址*/
{
	int i, key = 0;

	for (i = 0; i< 10; i++)
	{
		key = key + x.name[i];/*以数据元素关键字k本身或它的线性函数作为它的哈希地址*/
	}
	key = -key;
	return key;
}

/**
  * @brief  直接定址法获取名字的哈希地址
  * @param  x:哈希地址
  * @retval 返回电话号码哈希地址
  */
int Getkeyn(Datatype x)/*直接定址法获取电话号码的哈希地址*/
{
	int key = 0;
	long int z = x.num;
	while (z != 0)
	{
		key = key + z % 10;/*以数据元素关键字k本身或它的线性函数作为它的哈希地址*/
		z = z / 10;
	}
	return key;
}


hash.c

/************************************************
* 版权所有(C) 2022 浅笑醉红楼
* 
* 文件名称:hash.cpp
* 内容摘要:实现通讯录的显示、增加、查找、删除、修改等功能
* 其他说明:无
* 当前版本:1.0
* 作者:浅笑醉红楼
*************************************************/

/*------------------头文件引用------------------*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "header.h"
#include <conio.h>
/*------------------头文件引用------------------*/

/*----------------------------------------函数的声明----------------------------------------*/
void Displaymanu(HashTable ht, int n);					/*主菜单*/
void SJXR(HashTable ht);								/*数据写入*/
int SJDQ(HashTable ht);									/*数据读取*/
void add(HashTable ht, int n);							/*数据增加*/
void Delet(HashTable ht, int n);						/*数据删减*/
void change(HashTable ht, int n);						/*数据修改*/
int Search(HashTable ht, int n);						/*数据查找*/
void display(HashTable ht, int n);						/*显示所有员工数据*/
void fanhui(HashTable ht, int n);						/*退出函数*/
/*----------------------------------------函数的声明----------------------------------------*/

void main()		/*主函数*/				
{
	HashTable ht;
	int n;
	n = SJDQ(ht); 
	Displaymanu(ht, n);
}		

void Displaymanu(HashTable ht, int n)/*主菜单*/
{
	int choose;

	FILE* fp1;/*定义操作日志记录文件*/
	fp1 = fopen("Log.txt", "a");/*打开日志记录文本文件(“a”打开或新建一个文本文件,只允许在文件末尾追写,即只能增加不能删除)*/
	if (fp1 == NULL)/*打开文件失败*/
	{
		printf("打开文件出错!");
	}
	system("cls");/*清屏操作*/
	SJDQ(ht);/*员工数据读取*/
	/*-----------------------------人机交互-----------------------------*/
	printf("********************************************************\n");
	printf("*                       菜单                           *\n");
	printf("*1、增加员工信息                      2、删除员工信息  *\n");
	printf("*3、修改员工信息                      4、查找员工信息  *\n");
	printf("*5、打印所有员工信息                  6、退出程序      *\n");
	printf("********************************************************");
	/*-----------------------------人机交互-----------------------------*/
	printf("\n请输入序号来进行下一步操作:");
	scanf_s("%d", &choose);/*键盘输入功能选择*/
	switch(choose)/*功能选择*/
	{
	case 1:					/*增加*/	
		fprintf(fp1, "【%s】 %s %s\t\n", __DATE__, __TIME__, "增加员工信息");/*日志文件输出*/
		printf("【%s】 %s %s\t\n", __DATE__, __TIME__, "增加员工信息");
		add(ht, n);
		break;
	case 2:					/*删除*/	
		fprintf(fp1, "【%s】 %s %s\t\n", __DATE__, __TIME__, "员工删除");/*日志文件输出*/
		printf("【%s】 %s %s\t\n", __DATE__, __TIME__, "员工删除");
		Delet(ht, n);
		break;
	case 3:					/*修改*/		
		fprintf(fp1, "【%s】 %s %s\t\n", __DATE__, __TIME__, "修改员工信息");/*日志文件输出*/
		printf("【%s】 %s %s\t\n", __DATE__, __TIME__, "修改员工信息");
		change(ht, n);
		break;
	case 4:					/*查找*/
		fprintf(fp1, "【%s】 %s %s\t\n", __DATE__, __TIME__, "查找员工信息");/*日志文件输出*/
		printf("【%s】 %s %s\t\n", __DATE__, __TIME__, "查找员工信息");
		Search(ht, n);
		break; 
	case 5:					/*显示*/
		fprintf(fp1, "【%s】 %s %s\t\n", __DATE__, __TIME__, "打印所有员工信息");/*日志文件输出*/
		printf("【%s】 %s %s\t\n", __DATE__, __TIME__, "打印所有员工信息");
		fclose(fp1);
		display(ht, n);			/*哈希显示函数*/
		break;
	case 6:						/*退出*/
		exit (0);
	}
	Displaymanu(ht, n);			/*主菜单*/

}

/**
  * @brief  数据读取
  * @param  ht:哈希表
  * @retval 返回文件读取个数
  */
int SJDQ(HashTable ht)/*数据读取*/
{
	Datatype Hl[Hashsize];/*定义哈希表长度*/
	int n = 0;
	FILE* fp;			/*定义员工信息文本文件*/

	fp = fopen("employee.txt", "r");/*打开文本文件*/
	if(fp == NULL)/*打开文件失败*/
	{
		printf("文件打开失败");
		return -1;
	}

	while(fscanf(fp, "%s %d %s", &Hl[n].name, &Hl[n].num, &Hl[n].add) == 3)/*读取文本数据*/
	{
		n++;
	}
	fclose(fp);/*关闭文件*/
	CreatHash(ht, Hl, n);/*根据关键字个数n创建哈希表*/
	return n;
}

/**
  * @brief  员工数据增加
  * @param  ht:哈希表 n:增加员工个数
  * @retval 无
  */
void add(HashTable ht, int n)/*数据增加*/
{
	Datatype x;
	int addr, i;

	printf("请输入增加员工信息:\n");
	printf("请依次输入姓名,电话,地址:\n");
	scanf_s("%s %d %s", &x.name,10, &x.num, &x.add,50);
	addr = HashSearch(ht, x);/*在哈希表中查找*/
	if(addr < 0)/*没找到,则插入*/
	{
		i = HashInsert(ht, x);/*哈希插入*/
		printf("*****员工信息增加成功!*****\n");
	}
	else
	{	
			printf("已有数据,请重新输入\n");/*找到,不必插入*/
			add(ht, n);/*再次调用数据增加函数*/
	}
	SJXR(ht);/*数据写入*/
	fanhui(ht, n);/*退出此段程序*/
}

/**
  * @brief  员工信息的删除
  * @param  ht:哈希表 n:删除位置
  * @retval 无
  */
void Delet(HashTable ht, int n)/*数据删除*/
{
	Datatype x;
	int i;

	printf("请输入删除员工姓名:\n");
	scanf_s("%s", &x.name,10);/*键盘输入删除员工姓名*/
	i = HashDelete(ht, x);/*查找删除元素,并把删除结果赋值于i*/
	if(i == 1)/*哈希删除函数删除元素成功返回1*/
	{
		printf("*****员工信息删除成功!*****\n");
	}
	else
	{
		printf("没有找到员工数据,请重新输入\n");
		Delet(ht, n);/*再次调用删除函数*/
	}
	SJXR(ht);/*数据写入*/
	fanhui(ht, n);/*退出此段程序*/

}

/**
  * @brief  员工信息的修改
  * @param  ht:哈希表 n:修改位置
  * @retval 无
  */
void change(HashTable ht, int n)/*数据修改*/
{
	int addr = -1;
	int i, m; 
	Datatype x;

	printf("请输入查找联系人的方式:\n1、姓名\n2、电话号码\n输入除1、2外任意正数返回主菜单\n");
	scanf_s("%d", &m);
	if(m == 1)
	{
		printf("请输入查找姓名:\n");
		scanf_s("%s", &x.name,10);
		x.key = Getkey(x);/*获取哈希表键为Key的键值*/
		addr = HashFunc(x.key);/*获得哈希地址*/
		x.key = 1;
		while(ht[addr].data.key != 0 && ht[addr].data.key != x.key)
		{
			addr = Collision2(addr,i);/*没找到,处理冲突*/
		}
		if(strcmp(ht[addr].data.name, x.name) == 0)/*两个字符串相同,返回0*/
		{
			printf("找到员工信息:%s %d %s\n", ht[addr].data.name, ht[addr].data.num, ht[addr].data.add);
			printf("请输入修改后员工信息:\n");
			scanf_s("%s %d %s", &ht[addr].data.name,10, &ht[addr].data.num, &ht[addr].data.add,50);/*键盘输入员工修改信息*/
			printf("修改后员工信息为:%s %d %s\n", ht[addr].data.name, ht[addr].data.num, ht[addr].data.add);
			SJXR(ht);/*数据写入*/
		}
		else
		{
			printf("未找到该员工,请重新输入。\n");
			change(ht, n);/*调用修改函数*/
		}
	}
	if(m == 2)
	{
		printf("请输入查找电话号码:\n");
		scanf_s("%d", &x.num);/*键盘输入查找的电话号码*/
		for(i = 0; i < Hashsize; i++)
		{
			if(ht[i].data.num == x.num)
			{
				addr = i;
			}
		}
		if(addr == -1)
		{
				printf("未找到该号码,请重新输入\n");
				change(ht,n);
		}
		if(ht[addr].data.num == x.num)
		{
			printf("找到信息:%s %d %s\n", ht[addr].data.name, ht[addr].data.num, ht[addr].data.add);
			printf("请输入修改后信息:\n");
			scanf_s("%s %d %s", &ht[addr].data.name,10, &ht[addr].data.num, &ht[addr].data.add,50);
			printf("修改后信息为:%s %d %s\n", ht[addr].data.name, ht[addr].data.num, ht[addr].data.add);
			SJXR(ht);/*数据写入*/
		}
	}
	fanhui(ht, n);

}

/**
  * @brief  员工信息的查找
  * @param  ht:哈希表 n:查找位置
  * @retval 无
  */
int Search(HashTable ht, int n)/*数据查找*/
{
	int i, z, addr = -1;
	Datatype x;

	printf("请输入查找方式:\n1、姓名\n2、电话号码\n输入除1、2外任意正数返回主菜单\n");
	scanf_s("%d", &z);
	switch (z)
	{
	case 1:
		printf("请输入查找员工姓名:");
		scanf_s("%s", &x.name,10);
		x.key = Getkey(x);/*获取哈希表键为Key的键值*/
		addr = HashFunc(x.key);/*获得哈希地址*/
		x.key = 1;
		while(ht[addr].data.key != 0 && ht[addr].data.key != x.key)
		{
			addr = Collision2(addr,i);/*没找到,处理冲突*/
		}
		if(strcmp(ht[addr].data.name, x.name) == 0)/*对比输入员工信息和文本信息,一致返回0*/
		{
			printf("找到信息:%s %d %s\n", ht[addr].data.name, ht[addr].data.num, ht[addr].data.add);
		}
		else
		{
			printf("未找到该员工,请重新输入。");
			Search(ht, n);
		}
		break;
	case 2:
		printf("请输入查找电话号码:");
		scanf_s("%d", &x.num);
		for (i = 0; i < Hashsize; i++)
		{
			if (ht[i].data.num == x.num)
			{
				addr = i;
			}
		}
		if (addr == -1)
		{
			printf("未找到该号码,请重新输入。");
			Search(ht, n);
		}
		if (ht[addr].data.num == x.num)
		{
			printf("找到信息:%s %d %s\n", ht[addr].data.name, ht[addr].data.num, ht[addr].data.add);
		}
		break;
	}
	fanhui(ht, n);
}	

/**
  * @brief  员工信息数据写入
  * @param  ht:哈希表
  * @retval 无
  */
void SJXR(HashTable ht)/*数据写入*/
{
	FILE *fp;/*定义员工信息储存文件*/
	int i;

	fp = fopen("employee.txt", "w");/*打开文本文件*/
			if (fp == NULL)
			{
				printf("打开文件出错!");
			}
			for (i = 0; i < Hashsize; i++)
			{
				if (ht[i].data.key >0)
				{
					fprintf(fp, "%s %d %s\n",ht[i].data.name ,ht[i].data.num, ht[i].data.add);
			
				}
			}
			printf("*****操作成功*****\n");
			fclose(fp);/*关闭文件*/
}

/**
  * @brief  打印所有员工信息
  * @param  ht:哈希表 n:员工个数
  * @retval 无
  */
void display(HashTable ht, int n)/*显示所有员工数据*/
{
	int i;
	printf("所有数据为:\n");
	for(i = 0; i < Hashsize; i++)/*依次访问哈希表中员工数据,并显示*/
	{
		if(ht[i].data.key == 1)/*从哈希表数据域的第一个开始*/
		{
			printf("%s %d %s\n", ht[i].data.name, ht[i].data.num, ht[i].data.add);
	
		}
	}
	fanhui(ht, n);
}

/**
  * @brief  子程序退出函数
  * @param  ht:哈希表 n:键盘读入正数
  * @retval 无
  */
void fanhui(HashTable ht, int n)/*退出函数*/
{
	int A;

	while(1)
	{
		printf("**请输入任意正数返回目录**\n");
		scanf_s("%d", &A);
		if(A>0)
		{
			Displaymanu(ht, n);/*返回主菜单*/
			break;
		}
		else
		{
			printf("不是正数,请重新输入正数:");
			scanf_s("%d",&A);
			if (A > 0)
			{
				Displaymanu(ht, n);/*返回主菜单*/
				break;
			}
		}
	}
}

效果图

在这里插入图片描述

#include #include #include using namespace std; #define NULL 0 unsigned int key; //用来输入/输出文件流类 unsigned int key2; //key和key2分别是用做了电话号码和姓名的关键字 int *p; struct node //新建节点(用户姓名、地址、电话号码、指向下一个结点的指针 ) { char name[8],address[20]; char num[11]; node * next; }; typedef node* pnode; typedef node* mingzi; //声明了名字和电话两个指针 node **phone; node **nam; node *a; void hash(char num[11]) //以电话号码为关键字建立哈希函数 { int i = 3; key=(int)num[2]; while(num[i]!=NULL) { key+=(int)num[i]; i++; } key=key%20; } void hash2(char name[8]) //姓名为关键字建立哈希函数 { int i = 1; key2=(int)name[0]; while(name[i]!=NULL) { key2+=(int)name[i]; i++; } key2=key2%20; } //强制类型转换,将用户名的每一个字母的ASCLL码值相加并且除以20后的余数 node* input() //输入节点信息 ,建立结点,并将结点的next指针指空 { node *temp; temp = new node; temp->next=NULL; cout<<"输入姓名:"<>temp->name; cout<<"输入地址:"<>temp->address; cout<<"输入电话:"<>temp->num; return temp; } //对于指针类型返回的是地址 int apend() //添加节点 { node *newphone; node *newname; newphone=input(); newname=newphone; newphone->next=NULL; newname->next=NULL; hash(newphone->num); //利用哈希函数计算出对应关键字的存储地址 hash2(newname->name); newphone->next = phone[key]->next; //利用电话号码为关键字插入 phone[key]->next=newphone; //是采用链地址法,拉链法处理冲突的散列表结构 newname->next = nam[key2]->next; //利用用户名为关键字插入 nam[key2]->next=newname; return 0; } void create() //新建节点 { int i; phone=new pnode[20]; //动态创建对象数组,C++课本P188页 for(i=0;inext=NULL; } } void create2() //新建节点 { int i; nam=new mingzi[20]; for(i=0;inext=NULL; } } void list() //显示列表 { int i; node *p; for(i=0;inext; while(p) { cout<name<<'_'<address<<'_'<num<next; } } } void list2() //显示列表 { int i; node *p; for(i=0;inext; while(p) { cout<name<<'_'<address<<'_'<num<next; } } } void find(char num[11]) //在以电话号码为关键字的哈希表中查找用户信息 { hash(num); node *q=phone[key]->next; while(q!= NULL) { if(strcmp(num,q->num)==0) break; q=q->next; } if(q) cout<name<<"_" <address<<"_"<num<<endl; else cout<<"无此记录"<next; while(q!= NULL) { if(strcmp(name,q->name)==0) break; q=q->next; } if(q) cout<name<<"_" <address<<"_"<num<<endl; else cout<<"无此记录"<<endl; } void save() //保存用户信息 { int i; node *p; for(i=0;inext; while(p) { fstream iiout("out.txt", ios::out); //创建一个文件流对象:iiout iiout<name<<"_"<address<<"_"<num<next; } } } void menu() //菜单 { cout<<" 哈希表通讯录"<<endl; cout<<" 0.添加记录"<<endl; cout<<" 2.姓名散列"<<endl; cout<<" 3.查找记录"<<endl; cout<<" 4.号码散列"<<endl; cout<<" 5.清空记录"<<endl; cout<<" 6.保存记录"<<endl; cout<<" 7.退出系统"<>sel; if(sel==3) { cout<<"8姓名查询" <<endl;cout<<"9号码查询"<>b; if(b==9) {cout<<"请输入电话号码:"<>num; cout<<"输出查找的信息:"<<endl; find(num); } else {cout<<"请输入姓名:"<>name; cout<<"输出查找的信息:"<<endl; find2(name);}} if(sel==2) {cout<<"姓名散列结果:"<<endl; list2();} if(sel==0) {cout<<"请输入要添加的内容:"<<endl; apend();} if(sel==4) {cout<<"号码散列结果:"<<endl; list(); } if(sel==5) {cout<<"列表已清空:"<<endl; create();create2();} if(sel==6) { cout<<"通信录已保存:"<<endl; save();} if(sel==7) return 0; } return 0; }
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

✎浅笑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值