• 基于C++的社交应用的数据存储与实现


    社交应用的数据存储与实现

    目录

    一、 概述

    二、 整体架构

    三、 前台交互

    四、 后台存储

    (一) 、数据文件

    (二) 、数据结构

    五、 支持的操作及其算法

    (一) 、注册用户

    (二) 、登录用户

    (三) 、修改个人信息及修改密码

    (四) 、删除用户

    (五) 、关注/取消关注/移除粉丝

    (六) 、用户查找

    (七) 、发布消息

    (八) 、查看用户消息

    (九) 、查看新鲜事推送(即《课程设计》所指的消息列表)

    (十) 、转发消息

    (十一) 、删除消息

    六、 测试

    (一) 、正确性测试

    (二) 、性能测试

    七、 缺陷、展望及感悟

    一、概述
    本文介绍根据《社交应用的数据存储与实现》一文中所提要求设计的程序,该程序为32位Windows控制台程序,用C++写成,用VS编译,因调用了WinAPI不具备可移植性。截至目前该程序实现了《课程设计》文中的所有基本要求以及除关注推荐和折叠消息以外的所有扩展要求,此外还实现了一个简易的输入框(详见下文)。
    (一)数据文件
    一共使用四个文件,分别为user.idx(用户索引),user.dat(用户信息),user.rel(用户间关注关系),user.msg(用户所发消息)。使用C++标准库的文件流操作文件,在类初始化时打开文件,程序退出时关闭文件,程序运行途中由前台调用后台执行某事务时,在事务执行的最后调用fstream的flush方法将数据写入硬盘,既保证了内存与文件数据的一致性,又不至于每次调用write方法都flush而造成不必要的大量硬盘读写操作。
    (二)、数据结构
    1.用户信息及其索引
    用户的各项信息在设置时都有长度限制,存储在user.dat文件中,某用户的个人信息体现为一条定长的记录,用户记录一旦插入文件永不删除,删除用户时只对该记录做一标记以表示其已被删除,不可用于注册新用户,整个文件的结构为一系列用户记录按照用户注册的先后顺序依次排列,构成文件。用户记录如下所示:
    User:
    UserName 16 Bytes
    Password 32 Bytes
    Name 24 Bytes
    {Gender [1] 8 Bytes
    {Deleted[2]
    {Phone[3-8]
    Birthday 4 Bytes
    Hometown 24 Bytes
    InEdges 8 Bytes
    OutEdges 8 Bytes
    Message 8 Bytes
    InCount 4 Bytes
    OutCount 4 Bytes
    MessageCount 4 Bytes
    总计144字节
    其中,电话号码采用64位整数存放,由于电话号码即使算上区号也无法占满64位的整数,因此在前台限制了用户最多输入14位的电话号码,从而利用其最高及次高字节来存储性别以及是否已被删除;InEdges,OutEdges指向关注链表,Message指向消息链表,Incount,OutCount,MessageCount分别为粉丝计数、关注计数与消息计数,详见关注与消息的介绍部分。
    由于用户信息在user.dat文件中并没有任何有效的组织形式,为了快速找到某用户在user.dat中的位置,还建立了user.idx文件作为索引。在该索引文件内存储了一棵171阶B+树,Block大小为4096字节,其根节点总是位于该文件内的第一个Block。通过测试可以发现,VS实现的C++标准库在进行文件读写时,是以4096字节为单位进行读写的,此外Windows下NTFS文件系统默认的簇大小也是4096字节,因此以此作为参考,我将B+树的Block大小选定为了4096字节,以期与操作系统及标准库的buffer形成最好的配合。
    本文转载自:http://www.biyezuopin.vip/onews.asp?id=16549

    #include "stdafx.h"
    #include "user.h"
    
    #include 
    
    void User::initialize(string un, string pw, string na, bool ism, date bir, long long pn, string ht) {
    	username = un;
    	password = pw;
    	name = na;
    	ismale = ism;
    	birthday = bir;
    	phonenum = pn;
    	hometown = ht;
    
    	isdeleted = false;
    	in_edges = NULL;
    	out_edges = NULL;
    	message = NULL;
    	in_count = 0;
    	out_count = 0;
    	message_count = 0;
    }
    
    void User::initialize(char (&record)[RECORD_LENGTH]) {
    	username.clear();
    	for (int i = 0; i < 16; i++) {
    		if (record[i] == '\0')
    			break;
    		else
    			username.push_back(record[i]);
    	}
    	password.clear();
    	for (int i = PASSWORD; i < PASSWORD + 32; i++) {
    		if (record[i] == '\0')
    			break;
    		else
    			password.push_back(record[i]);
    	}
    	name.clear();
    	for (int i = NAME; i < NAME + 24; i++){
    		if (record[i] == '\0')
    			break;
    		else
    			name.push_back(record[i]);
    	}
    	memcpy(&phonenum, record + PHONE_GENDOR, 8);
    	if (phonenum >> 56)
    		ismale = true;
    	else
    		ismale = false;
    	phonenum &= 0x00FFFFFFFFFFFFFF;
    	if (phonenum >> 48)
    		isdeleted = true;
    	else
    		isdeleted = false;
    	phonenum &= 0x0000FFFFFFFFFFFF;
    	memcpy(&birthday.year, record + BIRTHDAY, 2);
    	birthday.month = record[BIRTHDAY + 2];
    	birthday.day = record[BIRTHDAY + 3];
    	hometown.clear();
    	for (int i = HOMETOWN; i < HOMETOWN + 24; i++){
    		if (record[i] == '\0')
    			break;
    		else
    			hometown.push_back(record[i]);
    	}
    	memcpy(&in_edges, record + IN_EDGES, 8);
    	memcpy(&out_edges, record + OUT_EDGES, 8);
    	memcpy(&message, record + MESSAGE, 8);
    	memcpy(&in_count, record + IN_COUNT, 4);
    	memcpy(&out_count, record + OUT_COUNT, 4);
    	memcpy(&message_count, record + MESSAGE_COUNT, 4);
    }
    
    string User::get_record() {
    	char record[RECORD_LENGTH] = {};
    	for (size_t i = 0; i < username.size(); i++)
    		record[i] = username[i];
    	for (size_t i = 0; i < password.size(); i++)
    		record[i + PASSWORD] = password[i];
    	for (size_t i = 0; i < name.size(); i++)
    		record[i + NAME] = name[i];
    	long long phone_gendor = phonenum;
    	if (ismale)
    		phone_gendor |= 0x0100000000000000; //最高字节标明性别
    	if (isdeleted)
    		phone_gendor |= 0x0001000000000000; //次高字节标明是否删除
    	memcpy(record + PHONE_GENDOR, &phone_gendor, 8);
    	memcpy(record + BIRTHDAY, &birthday.year, 2);
    	record[BIRTHDAY + 2] = birthday.month;
    	record[BIRTHDAY + 3] = birthday.day;
    	for (size_t i = 0; i < hometown.size(); i++)
    		record[i + HOMETOWN] = hometown[i];
    	memcpy(record + IN_EDGES, &in_edges, 8);
    	memcpy(record + OUT_EDGES, &out_edges, 8);
    	memcpy(record + MESSAGE, &message, 8);
    	memcpy(record + IN_COUNT, &in_count, 4);
    	memcpy(record + OUT_COUNT, &out_count, 4);
    	memcpy(record + MESSAGE_COUNT, &message_count, 4);
    	return string(record, RECORD_LENGTH);
    }
    
    void User::print_info(bool followed, bool fans) {
    	cout << username << "  关注:" << out_count << "  粉丝:" << in_count << "  微博:" << message_count << endl;
    	if (followed)
    		if (fans)
    			cout << "互相关注  ";
    		else
    			cout << "已关注  ";
    	else
    		if (fans)
    			cout << "我的粉丝  ";
    	if (ismale)
    		cout << "男  ";
    	else
    		cout << "女  ";
    	cout << birthday.year << '-' << int(birthday.month) << '-' << int(birthday.day) << "  ";
    	cout << "姓名: " << name << endl;
    	cout << "联系电话: ";
    	if (phonenum)
    		cout << phonenum;
    	else
    		cout << "无";
    	cout << "  来自: ";
    	if (!hometown.empty())
    		cout << hometown << endl;
    	else
    		cout << "无" << endl;
    }
    
    bool User::fits(Query &query, string &current_username) {
    	if (isdeleted || username == current_username)
    		return false;
    	if (query.name.second && name != query.name.first)
    		return false;
    	if (query.ismale.second && ismale != query.ismale.first)
    		return false;
    	if (query.birthday_start.second) {
    		if (query.birthday_end.second) {
    			if (birthday < query.birthday_start.first ||
    				birthday > query.birthday_end.first) //生日区间理解为闭区间
    				return false;
    		}
    		else if (birthday != query.birthday_start.first)
    			return false;
    	}
    	if (query.phonenum.second && phonenum != query.phonenum.first)
    		return false;
    	if (query.hometown.second && hometown != query.hometown.first)
    		return false;
    	return true;
    }
    
    bool date::operator==(const date &other) {
    	if (year == other.year && month == other.month &&
    		day == other.day)
    		return true;
    	else
    		return false;
    }
    
    bool date::operator!=(const date &other) {
    	if (year != other.year || month != other.month ||
    		day != other.day)
    		return true;
    	else
    		return false;
    }
    
    bool date::operator<(const date &other) {
    	if (year < other.year)
    		return true;
    	else if (year == other.year) {
    		if (month < other.month)
    			return true;
    		else if (month == other.month && day < other.day)
    			return true;
    	}
    	return false;
    }
    
    bool date::operator>(const date &other) {
    	if (year > other.year)
    		return true;
    	else if (year == other.year) {
    		if (month > other.month)
    			return true;
    		else if (month == other.month && day > other.day)
    			return true;
    	}
    	return false;
    }
    
    bool date::operator<=(const date &other) {
    	return !(*this > other);
    }
    
    bool date::operator>=(const date &other) {
    	return !(*this < other);
    }
    
    bool operator <(const User &a, const User &b) {
    	return a.username < b.username;
    }
    
    bool operator !=(const date &a, const date &b) {
    	if (a.year != b.year || a.month != b.month ||
    		a.day != b.day)
    		return true;
    	else
    		return false;
    }
    
    bool operator !=(const User &a, const User &b) {
    	if (a.birthday != b.birthday)
    		return true;
    	if (a.hometown != b.hometown)
    		return true;
    	if (a.in_count != b.in_count)
    		return true;
    	if (a.in_edges != b.in_edges)
    		return true;
    	if (a.isdeleted != b.isdeleted)
    		return true;
    	if (a.ismale != b.ismale)
    		return true;
    	if (a.message != b.message)
    		return true;
    	if (a.message_count != b.message_count)
    		return true;
    	if (a.name != b.name)
    		return true;
    	if (a.out_count != b.out_count)
    		return true;
    	if (a.out_edges != b.out_edges)
    		return true;
    	if (a.password != b.password)
    		return true;
    	if (a.phonenum != b.phonenum)
    		return true;
    	if (a.username != b.username)
    		return true;
    	return false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    三、RTMP协议 视频Chunk和音频Chunk到底长啥样?
    Centos7安装mysql(只需六步)
    vue2学习之动态组件
    SpringCloud 组件Gateway服务网关【gateway快速入门】
    HTML案例-1.标签练习
    腾讯开源 tRPC:多语言、高性能 RPC 开发框架
    数电学习(十、脉冲波形的产生和整形)(三)
    【leetcode】最近最少使用缓存
    Python接口自动化之unittest单元测试
    RDMA抓包
  • 原文地址:https://blog.csdn.net/newlw/article/details/126125945