通讯录管理系统(C++)
注意事项:
在代码测试之前必须先建立文件并导入内容以供测试使用,系统没有提示内容,用起来可能感到十分晦涩难以理解(可直接参考源代码,源代码还是非常浅显易懂的),这里仅供学习参考,不追求功能完善与实用性,如有问题或错误敬请谅解,欢迎积极指出。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<map>
//int x=0; //记录删掉联系人个数
using namespace std;
class Phone //数据类
{
string name;
string tel;
public:
Phone():name("0"),tel("0"){
};
Phone(string name1,string tel1):name(name1),tel(tel1){
}
string getname(){
return name;}
string gettel(){
return tel;}
void setname(string name1){
name=name1;}
void settel(string tel1){
tel=tel1;}
//尝试流输出,输出类对象的形式输出(运算符重载),因此这里去掉print函数(这两个作用一致)
//void print(){cout<<"name:"<<name<<endl<<"tel:"<<tel<<endl;}
friend istream&operator>>(istream&is,Phone&X);
friend ostream&operator<<(ostream&os,Phone&X);
};
istream&operator>>(istream&is,Phone&X)
{
is>>X.name;
while(is>>X.tel) //判断手机号位数
{
if(X.tel.size()==11&&X.tel[0]=='1')
break;
}
return is;
}
ostream&operator<<(ostream&os,Phone&X)
{
os<<"name:"<<X.name<<endl<<"tel:"<<X.tel<<endl;
return os;
}
class Operation //操作类
{
vector<Phone>lianxiren;
multimap<string,int>m1; //姓名
multimap<string,int>m2; //手机号
public: //操作函数
void zengtian();
void open();
void chaxun();
void set();
};
void Operation::zengtian()
{
/*string name,tel; cin>>name; while(cin>>tel) //判断手机号位数 { if(tel.size()==11) break; }*/
Phone X; //改用流输入,直接输入类对象(重载运算符)
cin>>X;
m1.insert(make_pair(X.getname(),lianxiren.size()));
m2.insert(make_pair(X.gettel(),lianxiren.size()));
//Phone obj(name,tel);
//lianxiren.push_back(obj);
lianxiren.push_back(X);
}
void Operation::open()
{
vector<Phone>::iterator it;
for(it=lianxiren.begin();it!=lianxiren.end();it++)
{
if(it->getname()!="0"&&it->gettel()!="0") //虚删打开时自动屏蔽“已删除”元素
//it->print();
cout<<*it;
if(it!=lianxiren.end()-1&&it->getname()!="0")
cout<<"-------------------------"<<endl; //不同联系人之间的分割线,但是只有一位联系人时不必显示(最后一位联系人后不显示)
}
//由该multimap测试数据得出联系人数据不能删,因为联系人信息删掉后下标前移,
//但是此时multimap中第二个值并没有发生改变,这样指向的值就会指向vector中下一个值,造成序号对应错误,修改时会产生问题。
//或许我们可以进行在multimap中find该删掉后的序号下标值(通过key值),然后进行从该值到结束遍历对multimap的second值减一,但是这样数据多时效率会大大降低。(像重建索引)
//另一种解决方法是假装删除实际不删除(不显示而已),那样可以对vector进行特殊处理,只是在“删掉后”不输出,但实际存在占一个内存值,这样multimap中又可以与vector的下标一一对应了。
//另外我们可以尝试对删除次数进行计数(这样每次操作时都通过该数使vector下标与multimap的second值进行对应)(需要定义全局变量为之计数,但是全局变量也不好。。。)
//这里我采用的是最后这种方法(最后我用了一下,发现其中的问题,就是当我们删除之后我们总是记录该最大值,却忘了当我们删数之前的那些下标处理,这样下标永远是无法对应的,因此思路错误。。。)
//最后我还是用的虚删的方式。
/* multimap<string,int>::iterator m1i; for(m1i=m1.begin();m1i!=m1.end();m1i++) { cout<<m1i->first<<" "<<m1i->second<<endl; } */
}
/* int main() { Operation op; string caozuo; while(1) { cin>>caozuo; if(caozuo=="zengtian") {op.zengtian();} if(caozuo=="open") {op.open();} if(caozuo=="end") break; } return 0; } */
/* void Operation::chaxun() { string caozuo,name,tel; cin>>caozuo; if(caozuo=="name") { cin>>name; multimap<string,int>::iterator m1i; m1i=m1.find(name); if(m1i!=m1.end()) lianxiren[m1i->second].print(); else cout<<"No Found!"<<endl; } if(caozuo=="tel") { cin>>tel; multimap<string,int>::iterator m2i; m2i=m2.find(tel); if(m2i!=m2.end()) lianxiren[m2i->second].print(); else cout<<"No Found!"<<endl; } } */
/* int main() { Operation op; string caozuo; while(1) { cin>>caozuo; if(caozuo=="zengtian") {op.zengtian();} if(caozuo=="open") {op.open();} if(caozuo=="chaxun") {op.chaxun();} if(caozuo=="end") break; } return 0; } */
void Operation::set() //先导入查询功能
{
string caozuo,name,tel,name_tel; //用name_tel来合并操作
string name1,tel1; //用来记录查找时最初的量(很重要的一步)
int ii;
//cin>>caozuo;
//if(caozuo=="name")
//{
cin>>name_tel;
multimap<string,int>::iterator m1i,m2i;
m1i=m1.end();m2i=m2.end();
m1i=m1.find(name_tel);
m2i=m2.find(name_tel);
if(m1i!=m1.end()||m2i!=m2.end())
{
if(m1i!=m1.end())
ii=m1i->second; //这里记录vector序号,以后用到时其实都可用ii表示,这样可以简化m1i->second,并且会避免m1i->second值的变化带来的问题
if(m2i!=m2.end())
ii=m2i->second;
//ii=ii-x;
//ii值-x变化了,但是这样每修改一次压入的ii值也会改变下标值,因此需要再定义一下值来记录该下标值使其保持不变,这样就可以使修改后下标依然对应
//lianxiren[ii].print();
cout<<lianxiren[ii];
cin>>caozuo;
while(1){
//这里允许多次修改与一次删除
name1=lianxiren[ii].getname(); //这里永远是首次操作时记录的序号,因此这里的name1,tel1与联系人信息绑定(不能说联系人信息不会变,这里的联系人信息随修改的变化而变化,是变量)
tel1=lianxiren[ii].gettel();
m1i=m1.find(name1); //每次操作后m1i,m2i的指向应该改变
m2i=m2.find(tel1);
if(caozuo=="operation")
{
int i=1,j=1;
cin>>caozuo;
while(1)
{
if(caozuo=="name")
{
i=0;
cin>>name;
lianxiren[ii].setname(name);
}
if(caozuo=="tel")
{
j=0;
cin>>tel;
lianxiren[ii].settel(tel);
}
if(caozuo=="end")
break;
cin>>caozuo;
}
if(i==0) {
m1.insert(make_pair(name,ii));}
if(j==0)
{
/* //当初测的是这样,这样会导致一个问题,每次修改完数据,lianxiren[ii].gettel()其实已经变了 //但是我们要删除修改之前的数据并补充新数据,如果用下面的代码测出的将会是删掉新的,保留原来的,相当于这一步没有执行任何操作 m2.insert(make_pair(tel,ii)); m2i=m2.find(lianxiren[ii].gettel()); m2.erase(m2i); */
m2.insert(make_pair(tel,ii));
m2i=m2.find(tel1); //删除原数据(每次while循环后都记录改变后的值,删除的是改变后的新值)
m2.erase(m2i);
}
if(i==0) {
m1.erase(m1i);}
}
if(caozuo=="delete")
{
vector<Phone>::iterator it;
it=lianxiren.begin();
it=it+ii; //这里删除用ii(起初的常值不会变)
//lianxiren.erase(it);
it->setname("0");
it->settel("0");
m1i=m1.find(name1);
m1.erase(m1i);
m2i=m2.find(tel1);
m2.erase(m2i);
//x++;
break;
}
if(caozuo=="return")
break;
cin>>caozuo;
}
}
else cout<<"No Found!"<<endl;
// }
/* if(caozuo=="tel") { cin>>tel; multimap<string,int>::iterator m2i; m2i=m2.find(tel); if(m2i!=m2.end()) lianxiren[m2i->second].print(); else cout<<"No Found!"<<endl; } */
}
int main()
{
Operation op;
//string caozuo;
freopen("1.txt","r",stdin); //操作记录从文件1.txt中输入数据
if(freopen("1.txt","r",stdin)==NULL)//(这里与fopen读取是否成功的判断方式不同)
{
cout<<"Can't read!"<<endl;
return 0;
}
freopen("2.txt","w",stdout); //操作输出结果输出到自动创建的文件2.txt中
/* while(1) //最后去掉“菜单” { cin>>caozuo; if(caozuo=="zengtian") {op.zengtian();} if(caozuo=="open") {op.open();} //if(caozuo=="chaxun") {op.chaxun();} if(caozuo=="set") {op.set();} if(caozuo=="end") break; } */
op.zengtian();
op.zengtian();
op.open();
op.set();
op.zengtian();
op.set();
op.open();
fclose(stdin);
fclose(stdout);
freopen("小光的通讯录.txt","w",stdout);//操作最后的通讯录结果(所有联系人)输出到"通讯录.txt"中
op.open();
fclose(stdout);
return 0;
}
需要的文件:
1.txt
ma 12345678900
li 12345654321
ma delete
guang 15275882925
li operation tel 12312312312 end return
2.txt
name:ma
tel:12345678900
-------------------------
name:li
tel:12345654321
name:ma
tel:12345678900
name:li
tel:12345654321
name:li
tel:12312312312
-------------------------
name:guang
tel:15275882925
小光的通讯录.txt
name:li
tel:12312312312
-------------------------
name:guang
tel:15275882925
部分功能测试:
输入电话合法性判断(必须为11位)
文件输入合理性(是否首位电话号码为“1”:)
打开(输出操作:)
删除操作:
改名操作:
文件无法读取操作:
最终结果:
(有些功能截图可能并不全面,具体可测试代码)