家谱:又称族谱、宗谱等。是一种以表谱形式,记载一个家族的世系繁衍及重要人物事迹的书。皇帝的家谱称玉牒,如新朝玉牒、皇宋玉牒。它以记载父系家族世系、人物为中心,由正史中的帝王本纪及王侯列传、年表等演变而来。
家谱是一种特殊的文献,就其内容而言,是中国五千年文明史中具有平民特色的文献,记载的是同宗共祖血缘集团世系人物和事迹等方面情况的历史图籍。家谱属珍贵的人文资料,对于历史学、民俗学、人口学、社会学和经济学的深入研究,均有其不可替代的独特功能。
##2. 项目简介 ##
预备知识:
这些知识只是附加功能用到的
系统基本功能(不一定全面):
项目设计思路:
家谱的用户分为管理员与普通用户,普通用户只有查询与查看家谱这两个功能,而管理员就可以对家谱进行修改。创建一个家谱类,对家谱的各种操作就是这个类的各种方法,家谱是根据二叉树来建立,按照“左孩子,右兄弟”这样的结构建立二叉树,所以所有的操作基本都是对二叉树的操作。
bool Genealogy::Create(Person **p,Person **root)
{
bool mate;
int tag;
*p = new Person;
if (!(*p))
{
return false;
}
(*p)->parent = NULL;
(*p)->firstchild = NULL;
(*p)->nexsilbing = NULL;
cout<<"本人:"<info)); //输入相关数据
cout<<"是否有配偶?(1表示有,0表示没有)"<mate));
}
else
{
(*p)->mate.name = "无";
}
Show(*root,0);
cout<<"1.添加"<<(*p)->info.name<<"的孩子"<info.name<<"的兄弟姐妹"<info.name<<"的后代及同胞"<firstchild),root);
if ((*p)->firstchild)
(*p)->firstchild->parent = *p;
cout<<"******是否添加"<<(*p)->info.name<<"的兄弟姐妹"<nexsilbing),root);
if ((*p)->nexsilbing)
(*p)->nexsilbing->parent = (*p)->parent;
}
else
(*p)->nexsilbing = NULL;
return true;
break;
case 2:
Create(&((*p)->nexsilbing),root);
if ((*p)->nexsilbing)
(*p)->nexsilbing->parent = (*p)->parent;
cout<<"******是否添加"<<(*p)->info.name<<"的孩子?(1表示是,0表示不添加)"<firstchild),root);
if ((*p)->firstchild)
(*p)->firstchild->parent = *p;
}
else
(*p)->firstchild = NULL;
return true;
break;
case 3:
(*p)->firstchild = NULL;
(*p)->nexsilbing = NULL;
return true;
break;
default:
(*p)->firstchild = NULL;
(*p)->nexsilbing = NULL;
return true;
break;
}
}
建立就是普通的递归建立二叉树操作,不过实现了一些自己的小点子。
void Genealogy::Show(Person* p,int depth)//depth:树的深度
{
if(p)
{
int i;
for (i = 1;i<=depth;i++)
{
cout<<"\t";
}
cout<info.name<firstchild,depth+1);
Show(p->nexsilbing,depth);
}
}
因为最后我们是要以一个树状图来显示整个家谱,所以需要传入一个depth来确定tab的个数,树每增加一层,depth也就加一。
bool Genealogy::Search(Person* p,const string name,int tag) //模糊查询
{
if(p)
{
if(name.length()>=4&&p->info.name.length()>=4)//如果名字长度大于四,就比较前四个
{
if(name.substr(0,4)==p->info.name.substr(0,4))
{
Display(p);
Search(p->firstchild,name,tag);
Search(p->nexsilbing,name,tag);
return true;
}
}
else
{
if(name==p->info.name)
{
Display(p);
return true;
}
else
{
Search(p->firstchild,name,tag);
Search(p->nexsilbing,name,tag);
return true;
}
}
}
return true;
}
bool Store(Person* p,ofstream& outfile)
{
if(p)
{
outfile<<"普通节点:"<info.name<info.gender<info.height<info.job<info.birthplace<info.birthday<info.live<info.deathday<info.age<info.education<info.great<mate.name!="无")
{
outfile<<"配偶:"<mate.name<mate.gender<mate.height<mate.job<mate.birthplace<mate.birthday<mate.live<mate.deathday<mate.age<mate.education<mate.great<firstchild,outfile);
Store(p->nexsilbing,outfile);
return true;
}
else
{
outfile<<"无节点"<
本来这里可以写一个函数避免代码冗杂的,但是先忽略(因为赶时间)
代码先放在这里,我们要结合这下面这个功能一起说为什么这么存文件。
bool Genealogy::Create(Person **p,ifstream& infile,streampos dir)//从文件读书据并创建树
{
string sign;
string name,job,birthplace,birthday,deathday,education;
string height;
string age;
string live,gender,great;
//streampos sp = infile.tellg(); //定位文件指针
streampos sp;
infile.seekg(dir);//读指针定位
if(!getline(infile,sign))
return true;
if(sign=="普通节点:")
{
*p = new Person;
if (!(*p))
{
return false;
}
(*p)->parent = NULL;
(*p)->firstchild = NULL;
(*p)->nexsilbing = NULL;
getline(infile,name);
getline(infile,gender);
getline(infile,height);
getline(infile,job);
getline(infile,birthplace);
getline(infile,birthday);
getline(infile,live);
getline(infile,deathday);
getline(infile,age);
getline(infile,education);
getline(infile,great);
(*p)->info.name = name;
(*p)->info.gender = stoi(gender);
(*p)->info.height = stod(height);
(*p)->info.job = job;
(*p)->info.birthplace = birthplace;
(*p)->info.birthday = birthday;
(*p)->info.live = stoi(live);
(*p)->info.deathday = deathday;
(*p)->info.age = stoi(age);
(*p)->info.education = education;
(*p)->info.great = stoi(great);
getline(infile,sign);
if(sign=="配偶:")
{
getline(infile,name);
getline(infile,gender);
getline(infile,height);
getline(infile,job);
getline(infile,birthplace);
getline(infile,birthday);
getline(infile,live);
getline(infile,deathday);
getline(infile,age);
getline(infile,education);
getline(infile,great);
(*p)->mate.name = name;
(*p)->mate.gender = stoi(gender);
(*p)->mate.height = stod(height);
(*p)->mate.job = job;
(*p)->mate.birthplace = birthplace;
(*p)->mate.birthday = birthday;
(*p)->mate.live = stoi(live);
(*p)->mate.deathday = deathday;
(*p)->mate.age = stoi(age);
(*p)->mate.education = education;
(*p)->mate.great = stoi(great);
}
(*p)->mate.name = "无"; //没有配偶就将配偶名字置为无
sp = infile.tellg();
Create(&((*p)->firstchild),infile,sp);
if((*p)->firstchild)
(*p)->parent = *p;
sp = infile.tellg();
Create(&((*p)->nexsilbing),infile,sp);
if((*p)->nexsilbing)
(*p)->parent = (*p)->parent;
}
else
{
*p = NULL;
return true;
}
}
这里做了太多重复性的工作,请看官忽略(毕竟为了赶时间)
这里有个重点,那就是文件指针的定位,毕竟这是一个递归创建树的过程,所以每次我创建好一个节点后必须知道创建下一个节点是从文件的哪个位置开始读取数据(总不可能每次都从文件开始处读吧)。除此之外还有一点需要注意,我们用getline()读数据返回值都是char*型的,但是想年龄这些数据时int型,所以一定要进行数据类型转化。
参考文档:
文件流指针的定位
文件流操作
数据类型转换
转化为int,有两种方式:string s = “123”;
int c = atoi(s.c_str());
或者
int c = stoi(s);
将string转化为double,也是两种方式。
string s = “123.5”;
double c = atof(s.c_str())
或者
double c = stod(s);
bug:
从文件建立树过后,我查看家谱就是这幅造型了:
很明显是出现了死循环,当时没有动脑子,就开始打断点debug,看了好几遍都觉得建立树的过程没有毛病。但是我忽略了一个细节,就两行代码:
这里的代码是我复制的我程序中其它地方的,然后没有注意,结构出现了严重的自己链接到自己的情况。下面是改过后的代码
这次真的是血淋淋的教训
很多地方都涉及当用户输入合法性的检测,这也是很多安全问题产生的根源,基本上所有的安全问题都是输入输出流的问题
匹配中文字符的正则表达式: [\u4e00-\u9fa5]
匹配双字节字符(包括汉字在内):[^\x00-\xff]
我在建立族谱时有一个对出生日期与死亡日期的输入,所以就需要检查用户输入的数据是否满足要求。
bool Detect(string temp) //检测日期是否正确
{
const regex pattern("\\d{2,4}[-\.]\\d{0,2}[-\.]\\d{0,2}");
return regex_match(temp,pattern);
}
这里的正则并不是限制的很严格,毕竟考虑到家谱中很多人的出生日期不详细。
void Detect(bool *temp)
{
while(!(cin>>*temp))
{
cin.clear(); //清除cin
//清除缓冲区的不合法字符
while(cin.get()!='\n'){
continue;
}
cout<<"提示:输入有误\n请重新输入:"<>*temp))
{
cin.clear();
while(cin.get()!='\n'){
continue;
}
cout<<"提示:输入有误\n请重新输入:"<>*temp))
{
cin.clear();
while(cin.get()!='\n'){
continue;
}
cout<<"提示:输入有误\n请重新输入:"<
原本的是准备将用户数据(账号密码)存在数据库中,登录就去验证输入是否正确,但是由于一些原因,就没有做。还有就是用爬虫去网络上爬一些现成的家谱存进数据库,然后用户可以自己选择查看那些家谱,但是网络上并没有现成的东西,所以只好放弃,所以也就没有用数据库,只是用文件来存储我们的数据。但是我还是想到了一个我一直想玩的东西-----人脸识别解锁。
准备工作
opencv+numpy安装
参考资料:
opencv+numpy安装与配置
安装过程中也出了一点问题(opencv与numpy版本不兼容)
解决办法:
http://blog.csdn.net/tchchan/article/details/76177505
http://blog.csdn.net/baobao3456810/article/details/52177316
具体实现:这里写链接内容
github地址:家谱管理系统
源码下载地址:这里写链接内容