答案:采用std::vector<Contact>存储联系人,结合文件I/O实现数据持久化,通过命令行菜单交互实现添加、查询、列出和保存功能。

在C++中实现命令行通讯录查询,核心在于合理的数据结构选择来存储联系人信息,利用标准输入输出进行用户交互,并结合文件I/O实现数据的持久化。这本质上是一个小型数据库应用,只不过我们自己来构建存储和查询逻辑。它考验的不仅是C++语法,更是对数据管理和用户体验的初步思考。
实现一个命令行通讯录查询,我们需要定义一个联系人结构体来存储姓名、电话等信息,然后用一个容器(比如
std::vector
对于命令行通讯录,数据结构的选择确实是第一步,也是决定后续操作效率的关键。我个人偏向于从最简单、最易理解的方案开始,然后根据实际需求逐步优化。
最初,一个
struct Contact
立即学习“C++免费学习笔记(深入)”;
struct Contact {
std::string name;
std::string phone;
std::string email; // 也可以有更多字段,比如地址、备注等
// 方便打印的函数
void display() const {
std::cout << "姓名: " << name << ", 电话: " << phone << ", 邮箱: " << email << std::endl;
}
// 用于文件读写的序列化/反序列化辅助函数
// 假设以逗号分隔
std::string serialize() const {
return name + "," + phone + "," + email;
}
static Contact deserialize(const std::string& line) {
Contact c;
size_t pos1 = line.find(',');
size_t pos2 = line.find(',', pos1 + 1);
if (pos1 != std::string::npos && pos2 != std::string::npos) {
c.name = line.substr(0, pos1);
c.phone = line.substr(pos1 + 1, pos2 - (pos1 + 1));
c.email = line.substr(pos2 + 1);
} else {
// 处理格式错误或不完整的情况,这里简单地赋空
c.name = line; // 至少保留一部分信息
c.phone = "";
c.email = "";
}
return c;
}
};接下来,管理这些
Contact
std::vector<Contact>
vector
std::vector<Contact> contacts; // 存储所有联系人
如果你对查询效率有更高的要求,尤其是在按姓名进行精确查找时,可以考虑
std::map<std::string, Contact>
std::unordered_map<std::string, Contact>
map
unordered_map
map
unordered_map
所以,我的建议是,先用
std::vector<Contact>
std::map
vector
map
一个好用的命令行工具,用户交互体验至关重要。这不仅仅是技术实现的问题,更是对用户习惯和心理的把握。我通常会设计一个清晰的菜单,并通过循环让用户可以持续操作,直到他们选择退出。
显示菜单: 每次操作完成后,或者在程序启动时,显示当前可用的命令。这能让用户一目了然。
void displayMenu() {
std::cout << "\n--- 通讯录管理系统 ---\n";
std::cout << "1. 添加联系人\n";
std::cout << "2. 查询联系人\n";
std::cout << "3. 列出所有联系人\n";
std::cout << "4. 保存并退出\n";
std::cout << "请输入您的选择: ";
}获取用户输入: 使用
std::cin
std::getline(std::cin >> std::ws, variable)
std::ws
getline
std::string choice_str; std::getline(std::cin >> std::ws, choice_str); // 读取整个行,包括可能的空格 int choice = std::stoi(choice_str); // 转换为整数
这里有个小坑,如果用户输入非数字,
std::stoi
try-catch
stringstream
命令解析与执行: 使用
if-else if
switch
switch (choice) {
case 1: addContact(); break;
case 2: searchContact(); break;
case 3: listAllContacts(); break;
case 4: running = false; break; // 退出循环
default: std::cout << "无效的选择,请重新输入。\n"; break;
}输入验证: 这是提升用户体验的关键。例如,添加电话号码时,可以简单检查是否全为数字;添加邮箱时,检查是否有
@
反馈信息: 每次操作后,给用户一个明确的反馈,比如“联系人添加成功!”、“未找到匹配联系人。”这让用户知道程序是否成功执行了他们的指令。
一个持续运行的循环是必不可少的,它让用户可以连续执行多个操作:
bool running = true;
while (running) {
displayMenu();
// 获取并处理用户输入
// ...
}这样的结构,加上清晰的提示和适当的错误处理,就能构建一个相对友好且功能完整的命令行交互界面。
数据的持久化是任何有状态应用程序的必备功能,对于通讯录来说,这意味着我们希望下次启动程序时,之前添加的联系人依然存在。在C++中,最常见的做法就是利用文件I/O,将数据写入硬盘。
我倾向于使用简单的文本文件,因为它可读性强,方便调试,也容易与其他工具集成(比如用文本编辑器直接查看或修改)。
选择文件格式:
张三,13800138000,zhangsan@example.com
nlohmann/json
保存数据:
std::ofstream
std::vector
Contact
void saveContactsToFile(const std::string& filename, const std::vector<Contact>& contacts) {
std::ofstream outFile(filename);
if (!outFile.is_open()) {
std::cerr << "错误: 无法打开文件 " << filename << " 进行写入。\n";
return;
}
for (const auto& contact : contacts) {
outFile << contact.serialize() << std::endl;
}
outFile.close();
std::cout << "通讯录已保存到 " << filename << "。\n";
}这里,
contact.serialize()
Contact
加载数据:
std::ifstream
Contact
std::vector
std::vector<Contact> loadContactsFromFile(const std::string& filename) {
std::vector<Contact> loadedContacts;
std::ifstream inFile(filename);
if (!inFile.is_open()) {
std::cerr << "注意: 无法打开文件 " << filename << ",将创建一个新的通讯录。\n";
return loadedContacts; // 返回空向量
}
std::string line;
while (std::getline(inFile, line)) {
if (!line.empty()) {
loadedContacts.push_back(Contact::deserialize(line));
}
}
inFile.close();
std::cout << "已从 " << filename << " 加载通讯录。\n";
return loadedContacts;
}这里的
Contact::deserialize(line)
Contact
关于错误处理: 文件I/O操作尤其需要注意错误处理。文件可能不存在、权限不足、磁盘空间已满等。检查
is_open()
std::cerr
std::cout
在程序启动时调用
loadContactsFromFile
saveContactsToFile
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <limits> // For numeric_limits
// Contact 结构体定义
struct Contact {
std::string name;
std::string phone;
std::string email;
void display() const {
std::cout << "姓名: " << name << ", 电话: " << phone << ", 邮箱: " << email << std::endl;
}
std::string serialize() const {
// 简单地用逗号分隔,如果字段本身包含逗号,这里需要更复杂的转义逻辑
return name + "," + phone + "," + email;
}
static Contact deserialize(const std::string& line) {
Contact c;
size_t pos1 = line.find(',');
size_t pos2 = line.find(',', pos1 + 1);
if (pos1 != std::string::npos && pos2 != std::string::npos) {
c.name = line.substr(0, pos1);
c.phone = line.substr(pos1 + 1, pos2 - (pos1 + 1));
c.email = line.substr(pos2 + 1);
} else if (pos1 != std::string::npos) { // 只有姓名和电话
c.name = line.substr(0, pos1);
c.phone = line.substr(pos1 + 1);
c.email = "";
} else { // 只有姓名
c.name = line;
c.phone = "";
c.email = "";
}
return c;
}
};
// 全局的联系人列表
std::vector<Contact> contacts;
const std::string FILENAME = "contacts.txt"; // 数据文件
// 函数声明
void displayMenu();
void addContact();
void searchContact();
void listAllContacts();
void saveContactsToFile();
void loadContactsFromFile();
int main() {
loadContactsFromFile(); // 程序启动时加载数据
bool running = true;
while (running) {
displayMenu();
std::cout << "请输入您的选择: ";
std::string choice_str;
std::getline(std::cin >> std::ws, choice_str); // 使用 >> std::ws 跳过前一个换行符
try {
int choice = std::stoi(choice_str);
switch (choice) {
case 1: addContact(); break;
case 2: searchContact(); break;
case 3: listAllContacts(); break;
case 4:
saveContactsToFile();
running = false;
std::cout << "程序已退出。再见!\n";
break;
default:
std::cout << "无效的选择,请重新输入。\n";
break;
}
} catch (const std::invalid_argument& e) {
std::cerr << "输入无效,请确保输入的是数字。\n";
} catch (const std::out_of_range& e) {
std::cerr << "输入的数字太大或太小,请重新输入。\n";
}
std::cout << std::endl; // 每次操作后换行,美观
}
return 0;
}
void displayMenu() {
std::cout << "--- 通讯录管理系统 ---\n";
std::cout << "1. 添加联系人\n";
std::cout << "2. 查询联系人\n";
std::cout << "3. 列出所有联系人\n";
std::cout << "4. 保存并退出\n";
}
void addContact() {
Contact newContact;
std::cout << "请输入姓名: ";
std::getline(std::cin >> std::ws, newContact.name);
std::cout << "请输入电话: ";
std::getline(std::cin >> std::ws, newContact.phone);
std::cout << "请输入邮箱 (可选,留空请直接回车): ";
std::getline(std::cin >> std::ws, newContact.email); // 允许为空
// 简单验证,确保姓名不为空
if (newContact.name.empty()) {
std::cout << "姓名不能为空,添加失败。\n";
return;
}
contacts.push_back(newContact);
std::cout << "联系人 '" << newContact.name << "' 添加成功!\n";
}
void searchContact() {
std::cout << "请输入要查询的姓名或电话 (支持模糊查询): ";
std::string query;
std::getline(std::cin >> std::ws, query);
bool found = false;
for (const auto& contact : contacts) {
// 简单的模糊查询,判断姓名或电话是否包含查询字符串
if (contact.name.find(query) != std::string::npos ||
contact.phone.find(query) != std::string::npos) {
contact.display();
found = true;
}
}
if (!found) {
std::cout << "未找到匹配的联系人。\n";
}
}
void listAllContacts() {
if (contacts.empty()) {
std::cout << "通讯录为空。\n";
return;
}
std::cout << "--- 所有联系人 ---\n";
for (const auto& contact : contacts) {
contact.display();
}
std::cout << "-------------------\n";
}
void saveContactsToFile() {
std::ofstream outFile(FILENAME);
if (!outFile.is_open()) {
std::cerr << "错误: 无法打开文件 " << FILENAME << " 进行写入。\n";
return;
}
for (const auto& contact : contacts) {
outFile << contact.serialize() << std::endl;
}
outFile.close();
std::cout << "通讯录已保存到 " << FILENAME << "。\n";
}
void loadContactsFromFile() {
std::ifstream inFile(FILENAME);
if (!inFile.is_open()) {
std::cerr << "注意: 无法打开文件 " << FILENAME << ",将创建一个新的通讯录。\n";
return; // 返回空向量,表示从头开始
}
std::string line;
while (std::getline(inFile, line)) {
if (!line.empty()) {
contacts.push_back(Contact::deserialize(line));
}
}
inFile.close();
std::cout << "已从 " << FILENAME << " 加载通讯录。\n";
}以上就是C++如何实现命令行通讯录查询的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号