目录
一、本节作业要求
1.1 作业功能要求
(1)插入数据
(2)查找数据
(3)删除数据
1.2 作业实现要求
1.3 本节作业的意义
二、作业需求分析
2.1 这个“缓存数据库”本质上是什么
2.2 为什么使用两个 vector
三、实现思路
3.1 用户输入命令
3.2 命令拆分
3.3 数据存储
3.4 数据查找
3.5 数据删除
四、完整代码实现与运行
4.1 代码实现
4.2 代码运行效果演示
4.2.1 输入示例
4.2.2 输出示例
4.2.3 结果分析
五、代码详细解析
5.1 头文件与命名空间
5.2 定义两个 vector
5.3 使用 getline 获取整行命令
5.4 istringstream iss(input); 到底是什么意思
5.4.1 它的本质是什么
5.4.2 iss >> cmd; 读取了什么
5.5 insert 插入命令的处理过程
5.6 查找 key 的逻辑
5.6.1 index = -1 表示什么
5.6.2 if (keys[i] == key) 是什么意思
5.6.3 为什么 keys[i] 不是一个字母
5.7 为什么“前面没存进去”却还能比较
六、本节重难点与常见疑问
6.1 getline(cin, input) 和 cin >> input 有什么区别
6.2 istringstream 为什么有用
6.3 keys 和 key 的区别
七、小结
一、本节作业要求
本节作业的核心目标是:使用 C++ 基础语法,实现一个简易缓存数据库程序。
这个“小型缓存数据库”并不是真正意义上的数据库系统,而是一个入门级的键值存储(Key-Value)模拟程序。
用户通过输入命令,可以完成数据的插入、查询和删除。
1.1 作业功能要求
根据本次作业要求,程序需要支持以下功能:
(1)插入数据
用户输入:
insert xcj datastring表示将:
- key:
xcj - value:
datastring
存入缓存数据库中,并提示“存储成功”。
(2)查找数据
用户输入:
get xcj程序需要根据关键字xcj找到对应的值,并输出:
datastring(3)删除数据
用户输入:
delete xcj程序需要删除关键字xcj对应的数据,并提示删除成功。
1.2 作业实现要求
本次作业还要求实现时满足以下几点:
(1)使用getline获取用户输入的一整行命令
(2)使用两个vector存储数据
1)一个vector存key
2)一个vector存value
(3)程序主体使用main函数完成,不额外拆分函数也可以
(4)完成命令解析、查找逻辑和删除逻辑
1.3 本节作业的意义
把前面学过的知识点串起来,完成一个真正可以运行的小项目。
虽然这个程序规模不大,但它几乎涵盖了 C++ 初学阶段很多关键知识点,比如:
getline读取整行输入string字符串处理istringstream命令拆分vector<string>存储多个字符串for循环遍历查找if条件判断erase删除元素
可以说,这一节就是前面基础内容的一次综合练习。
二、作业需求分析
2.1 这个“缓存数据库”本质上是什么
从本质上来说,这个作业是在实现一个最简单的键值对存储系统。
比如输入:
insert xcj datastring insert key2 data2 insert key3 data3123那么程序内部要保存的数据关系就是:
xcj -> datastring key2 -> data2 key3 -> data3123这和真实数据库中的“键值映射”思想很像,只不过这里我们没有用真正的数据库,而是用vector来模拟。
2.2 为什么使用两个vector
题目要求:
- 一个
vector存放 key - 一个
vector存放 value
因此可以这样设计:
vector<string> keys; vector<string> values;它们通过相同下标建立对应关系:
keys[0] <-> values[0] keys[1] <-> values[1] keys[2] <-> values[2]例如:
keys[0] = "xcj"; values[0] = "datastring";表示:
xcj -> datastring三、实现思路
3.1 用户输入命令
因为用户输入的命令可能是这样:
insert xcj datastring这里一整行里包含多个单词,中间还有空格。
如果使用普通的:
cin >> input;那么只能读取到第一个单词,不能完整获取整行命令。
因此这里要使用:
getline(cin, input);这样就可以把整条命令完整读进来。
3.2 命令拆分
读取到整行字符串之后,还需要把它拆成:
- 命令名
insert - key
xcj - value
datastring
这里使用:
istringstream iss(input);把字符串转成“字符串输入流”,然后再像cin一样读取:
string cmd; iss >> cmd;这样就能先读出命令。
3.3 数据存储
程序运行过程中,用户可能会不断插入数据,因此我们使用动态容器vector<string>:
vector<string> keys; vector<string> values;当执行insert时,把 key 放入keys,把 value 放入values。
3.4 数据查找
查找的核心就是遍历keys:
for (int i = 0; i < (int)keys.size(); ++i) { if (keys[i] == key) { index = i; break; } }找到后,再通过相同下标去values中取值。
3.5 数据删除
删除时,既要删keys中对应位置的 key,也要删values中对应位置的 value:
keys.erase(keys.begin() + index); values.erase(values.begin() + index);这样才能保证两个vector的下标继续一一对应。
四、完整代码实现与运行
4.1 代码实现
#include <iostream> #include <string> #include <vector> #include <sstream> using namespace std; int main(int argc, char* argv[]) { vector<string> keys; // 存放 key vector<string> values; // 存放 value cout << "==============================" << endl; cout << " 简易缓存数据库系统启动成功" << endl; cout << " 支持命令:insert / get / delete / show / exit" << endl; cout << "==============================" << endl; string input; while (true) { cout << "\n请输入命令 > "; getline(cin, input); if (input.empty()) { continue; } istringstream iss(input); string cmd; iss >> cmd; // 1. 插入数据 if (cmd == "insert") { string key; string value; iss >> key; getline(iss >> ws, value); // 读取剩余内容作为 value,支持空格 if (key.empty() || value.empty()) { cout << "命令格式错误,正确格式:insert key value" << endl; continue; } int index = -1; for (int i = 0; i < (int)keys.size(); ++i) { if (keys[i] == key) { index = i; break; } } if (index != -1) { values[index] = value; cout << "更新成功:[" << key << "] = " << value << endl; } else { keys.push_back(key); values.push_back(value); cout << "存储成功:[" << key << "] = " << value << endl; } } // 2. 查找数据 else if (cmd == "get") { string key; iss >> key; if (key.empty()) { cout << "命令格式错误,正确格式:get key" << endl; continue; } int index = -1; for (int i = 0; i < (int)keys.size(); ++i) { if (keys[i] == key) { index = i; break; } } if (index != -1) { cout << "输出值:" << values[index] << endl; } else { cout << "未找到 key: " << key << endl; } } // 3. 删除数据 else if (cmd == "delete") { string key; iss >> key; if (key.empty()) { cout << "命令格式错误,正确格式:delete key" << endl; continue; } int index = -1; for (int i = 0; i < (int)keys.size(); ++i) { if (keys[i] == key) { index = i; break; } } if (index != -1) { keys.erase(keys.begin() + index); values.erase(values.begin() + index); cout << "显示删除成功" << endl; } else { cout << "删除失败,未找到 key: " << key << endl; } } // 4. 显示所有数据 else if (cmd == "show") { if (keys.empty()) { cout << "当前缓存为空" << endl; } else { cout << "当前缓存内容如下:" << endl; for (int i = 0; i < (int)keys.size(); ++i) { cout << keys[i] << " -> " << values[i] << endl; } } } // 5. 退出程序 else if (cmd == "exit") { cout << "程序退出成功" << endl; break; } // 6. 未知命令 else { cout << "未知命令,请重新输入" << endl; } } return 0; }4.2 代码运行效果演示
4.2.1 输入示例
insert xcj datastring insert key2 data2 insert key3 data3123 get xcj delete key2 show exit4.2.2 输出示例
存储成功:[xcj] = datastring 存储成功:[key2] = data2 存储成功:[key3] = data3123 输出值:datastring 显示删除成功 当前缓存内容如下: xcj -> datastring key3 -> data3123 程序退出成功4.2.3 结果分析
从运行结果可以看出:
(1)insert可以成功插入数据
(2)get可以根据 key 取出对应的 value
(3)delete可以删除指定 key 对应的数据
(4)show可以查看当前缓存中的全部内容
(5)整个程序已经具备了一个最基础键值缓存系统的雏形
五、代码详细解析
5.1 头文件与命名空间
#include <iostream> #include <string> #include <vector> #include <sstream> using namespace std;(1)<iostream>
用于输入输出,比如cin和cout
(2)<string>
用于字符串类型string
(3)<vector>
用于动态数组容器vector
(4)<sstream>
用于字符串流istringstream,把一整行字符串拆分成多个部分
5.2 定义两个vector
vector<string> keys; vector<string> values;这里定义了两个字符串类型的动态数组。
(1)keys用来保存关键字
(2)values用来保存对应的值
例如执行:
insert xcj datastring后,程序内部可能变成:
keys[0] = "xcj"; values[0] = "datastring";5.3 使用getline获取整行命令
string input; getline(cin, input);这一句的作用是:
从控制台读取用户输入的一整行内容。
比如用户输入:
insert xcj datastring那么:
input = "insert xcj datastring"如果这里不用getline,而用cin >> input,那么只能读到:
insert后面的内容就拿不到了。
5.4istringstream iss(input);到底是什么意思
这是本节一个非常容易疑惑的点。
istringstream iss(input); string cmd; iss >> cmd;5.4.1 它的本质是什么
可以把它理解成:
cin:从键盘读取iss:从字符串里读取
也就是说,前面用getline读到的是一整个字符串:
"insert xcj datastring"而istringstream的作用,就是把这个字符串变成“可按空格一个个读取的流”。
5.4.2iss >> cmd;读取了什么
这句会读取第一个单词,也就是命令名:
cmd = "insert"之后再继续读取,就可以得到 key 和 value。
所以这三句代码的整体作用就是:
把用户输入的一整行命令拆开,先取出第一个单词作为命令。
5.5insert插入命令的处理过程
if (cmd == "insert") { string key; string value; iss >> key; getline(iss >> ws, value);这里的逻辑是:
(1)先读命令后的第一个单词作为key
(2)再把剩余内容作为value
例如输入:
insert xcj datastring那么:
key = "xcj" value = "datastring"如果用户输入的是:
insert name hello world那么这里的:
getline(iss >> ws, value);还能把带空格的内容一起读出来:
value = "hello world"5.6 查找 key 的逻辑
int index = -1; for (int i = 0; i < (int)keys.size(); ++i) { if (keys[i] == key) { index = i; break; } }这一段是整个程序查找逻辑的核心。
5.6.1index = -1表示什么
这里用index表示“目标 key 所在的位置”。
- 如果找到了,就让
index = i - 如果没找到,就保持
-1
因此-1可以理解为一个“未找到标记”。
5.6.2if (keys[i] == key)是什么意思
这句是在比较:
keys[i]:第i个存进去的 keykey:当前用户输入的 key
如果两者相等,说明找到了对应位置。
例如:
keys[0] = "xcj" keys[1] = "key2"用户输入:
get key2那么:
key = "key2"循环时会依次比较:
keys[0] == "key2" // false keys[1] == "key2" // true找到后:
index = 1; break;5.6.3 为什么keys[i]不是一个字母
因为:
vector<string> keys;这里keys里面存的是很多个string 字符串,不是字符。
所以:
keys[i]取出来的是第i个字符串,比如"xcj"、"key2",而不是单个字符。
这一点要和下面这种情况区分开:
string s = "xcj"; s[0]这里的s[0]才是单个字符'x'。
也就是说:
(1)keys[i]:取vector里的第i个字符串
(2)str[i]:取字符串里的第i个字符
这两个写法表面看起来很像,但含义完全不同。
5.7 为什么“前面没存进去”却还能比较
很多同学会觉得:
前面只是定义了
vector<string> keys;,并没有马上push_back数据,那后面怎么比较?
答案是:
空的时候其实不会比较。
因为查找代码写的是:
for (int i = 0; i < (int)keys.size(); ++i)如果现在keys是空的,那么:
keys.size() == 0循环就变成:
for (int i = 0; i < 0; ++i)一次都不会执行。
也就是说:
- 程序刚启动时,
keys只是一个空容器 - 只有用户执行
insert之后,才真正往里面放入内容 - 后面执行
get或delete时,才会遍历已有的数据进行比较
所以不是“没存进去先比较”,而是“先定义空容器,后续有数据了再比较”。
六、本节重难点与常见疑问
6.1getline(cin, input)和cin >> input有什么区别
(1)cin >> input
读取到空格就停止
(2)getline(cin, input)
读取整行内容,直到按下回车
对于本节这种命令行程序来说,命令一般都是一整行,因此更适合用getline。
6.2istringstream为什么有用
因为它可以把整行字符串再次拆开读取。
例如:
string input = "get xcj"; istringstream iss(input); string cmd, key; iss >> cmd >> key;结果就是:
cmd = "get" key = "xcj"它特别适合做命令解析、配置行解析、简单脚本输入处理。
6.3keys和key的区别
这是变量命名上很容易混的地方。
(1)key
表示一个单独的关键字
(2)keys
表示很多个 key 组成的容器
这里同理:
key:一个键keys:很多键
七、小结
本节通过一个“简易缓存数据库”作业,把前面学过的多个 C++ 基础知识点串联了起来。
我们不仅练习了:
stringvectorgetlineistringstreamforiferase
还真正完成了一个可运行的小项目。
更重要的是,这个作业背后体现的是一种很典型的程序设计思想:
把用户输入解析为命令,再根据命令操作程序内部的数据结构。
虽然这里实现得还比较简单,但它已经非常接近很多真实程序的基本工作流程了,比如:
- 命令行工具
- 配置解析器
- 简易管理系统
- 键值缓存结构