1. ホーム
  2. c++

系図管理システムc++実装

2022-02-23 11:44:11
<パス

1. 背景


系図(けいず)。家系図、氏族図などとも呼ばれる。一族の血統再現や重要人物の行状を記録した表形式の系図書である。新王朝玉璽、皇宋玉璽など、天皇の系図を玉璽と呼ぶ。父方の家系や人物の記録が中心で、正史の天皇の本紀や王侯の伝記・年表から発展したものである。
家系図は特殊な文献であり、内容的には中国文明5000年の歴史の中で、同じ祖先の血族集団の血統人物と行いの歴史図を記録した文明的特徴を持つ文書である。系譜は、歴史、民俗、人口学、社会学、経済学などを深く研究する上で、独自のかけがえのない機能を持つ貴重な人的資源である。
##2. プロジェクトの説明 ##

前提条件となる知識

  1. c++の基本的な構文
  2. データ構造-木
  3. 正規表現
  4. mysqlデータベースの基本操作

この知識はアドオンのみで使用されます

  1. Pythonの基本的な構文
  2. python libraries: osopencviarequests
  3. http関連知識
  4. コンピュータコーディングに関する一般的な知識

システムの基本的な機能(必ずしも網羅的でなくてもよい)。

  1. 家系図の作成
  2. 追加、削除、変更、チェック
  3. 統計情報
  4. アーカイビング

プロジェクトデザインのアイデア
系図のユーザーは管理者と一般ユーザーに分かれ、一般ユーザーは系図の照会と閲覧の2つの機能のみを持ち、管理者は系図を修正することができます。系図は二分木をベースにしており、「左子、右弟」の構造に従って構築されているため、基本的にすべての操作は二分木に対する操作となります。

3. プロジェクト機能の紹介


  • ファミリーツリーを作成する
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<<"myself:"<<<endl;
	input(&((*p)->info)); //enter relevant data
	cout<<"Is there a spouse? (1 means yes, 0 means no)"<<<endl;
	Detect(&mate); //check input legitimacy
	if (mate)
	{
		cout<<"mate:"<<<endl;
		input(&((*p)-> mate));
	}
	else
	{
		(*p)->mate.name = "none";
	}
	Show(*root,0);
	cout<<"1. add"<<(*p)->info.name<<"s child"<<<endl;
	cout<<"2. Add"<<(*p)->info.name<<"siblings"<<<endl;
	cout<<"3. No more adding "<<(*p)->info.name<<"s descendants and siblings"<<<endl;
	cout<<"Enter your selection:";
	Detect(&tag);
	switch(tag)
	{
	case 1:
		Create(&((*p)-> firstchild),root);
		if ((*p)->firstchild)
			(*p)->firstchild->parent = *p;
		cout<<"****** Does add"<<>(*p)->info.name<<"sibling"<<<endl;
		Detect(&tag);
		if(tag)
		{
			Create(&((*p)->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<<"Does ****** add "<<(*p)->info.name<<" children? (1 means yes, 0 means not added)"<<<endl;
		Detect(&tag);
		if(tag)
		{
			Create(&((*p)->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;
	}
}


<イグ
Buildは通常の再帰的なbuildバイナリツリー操作ですが、いくつかの独自の小さなアイデアを実装しています。

  • 系図を表示する
void Genealogy::Show(Person* p,int depth)//depth: depth of the tree
{
	if(p)
	{
		int i;
		for (i = 1;i<=depth;i++)
		{
			cout<<"\t";
		}
		cout<<p->info.name<<endl;
		Show(p->firstchild,depth+1);
		Show(p->nexsilbing,depth);
	}
}


最終的には家系図全体をツリーとして表示するので、タブの数を決めるために深さを渡す必要があり、ツリーのレベルが増えるごとに深さが1ずつ追加されます。

  • ファジィクエリ
    このシステムには、正確なクエリと、これから紹介するファジーなクエリの2種類があります。ファジー・クエリーは実際にはかなり粗雑なもので、一般的なアイデアは、家系図にある人物の名前の最初の2つの単語が、ユーザーが入力したデータと同じであれば、それを出力することである。
bool Genealogy::Search(Person* p,const string name,int tag) //fuzzy query
{
	if(p)
	{ if(p)
		if(name.length()>=4&&p->info.name.length()>=4)//If name length is greater than four, compare the first four
		{
			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<<"Normal node:"<<<endl;
		outfile<<p->info.name<<endl;
		outfile<<p->info.gender<<endl;
		outfile<<p->info.height<<endl;
		outfile<<p->info.jobs<<endl;
		outfile<<p->info.birthplace<<endl;
		outfile<<p->info.birthday<<endl;
		outfile<<p->info.live<<endl;
		outfile<<p->info.deathday<<endl;
		outfile<<p->info.age<<endl;
		outfile<<p->info.education<<endl;
		outfile<<p->info.great<<endl;
		if(p->mate.name!="none")
		{
			outfile<<"mate:"<<<endl;
			outfile<<p->mate.name<<endl;
			outfile<<p->mate.gender<<endl;
			outfile<<p->mate.height<<endl;
			outfile<<p->mate.job<<endl;
			outfile<<p->mate.birthplace<<endl;
			outfile<<p->mate.birthday<<endl;
			outfile<<p->mate.live<<endl;
			outfile<<p->mate.deathday<<endl;
			outfile<<p->mate.age<<endl;
			outfile<<p->mate.education<<endl;
			outfile<<p->mate.great<<endl;
		}
		else
		{
			outfile<<"no mate"<<<endl;
		}	
		Store(p->firstchild,outfile);
		Store(p->nexsilbing,outfile);
		return true;
	}
	else
	{
		outfile<<"no node"<<<endl;
		return true;
	}
}


コードの重複を避けるために、ここに関数を書いてもよかったのですが、とりあえず無視します(急いでいるので)
コードはとりあえずここで、なぜこんなにファイルを保存しているのかというと、下のこの関数と組み合わせるのです。

  • ファイルから系図を作成する
    このパートは最も時間がかかったと言えます。主に小さなバグがあり、それを見つけるのに数時間を要したからです。
    まず、コードを見る。
bool Genealogy::Create(Person **p,ifstream& infile,streampos dir)//read data from file and create tree
{
	string sign;
	string name,job,birthplace,birthday,deathday,education;
	string height;
	string age;
	string live,gender,great;
	//streampos sp = infile.tellg(); // locate the file pointer
	streampos sp;
	infile.seekg(dir);//read pointer to locate
	if(!getline(infile,sign))
		return true;
	if(sign=="Common node:")
	{
		*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=="spouse:")
		{
			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 = "none"; // no spouse will set spouse name to none
		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への変換は、2つの方法で行われます。

文字列 s = "123"。

int c = atoi(s.c_str());

または

int c = stoi(s);

文字列をdoubleに変換する、これも2通り。

文字列 s = "123.5";

double c = atof(s.c_str())

または

double c = stod(s);

のバグです。
ファイルからツリーを構築した後、このような形で家系図を見てみました。

デッドループがあることは明らかですが、その後、脳がなく、ポイントデバッグを中断し始め、何回か見て、ツリーの構築のプロセスは間違っていないことを感じています。しかし、私は1つの詳細、コードのわずか2行を見落とした。


ここのコードは、私がプログラムの他の場所からコピーしたもので、その後注意を払わず、構造が自分自身にひどくリンクしているように見えたものです。以下は、変更後のコードです。

今度こそ本当に血のにじむような授業

  • 家族の追加
    家族を追加する方法は、家族の子供を追加する方法と、家族の兄弟を追加する方法があるので、詳しいコードは掲載しません。

4. 乾燥


これは多くのセキュリティ問題の根源であり、基本的にすべてのセキュリティ問題は、入力と出力のストリームに関する問題です。

<ブロッククオート

漢字にマッチする正規表現。[を使用します。]
全角文字(漢字を含む)にマッチします。[^x00-xxxff] です。

系図を作るときに生年月日と死亡日の入力があったので、その後、ユーザーが入力したデータが条件を満たしているかどうかをチェックする必要がありました。

bool Detect(string temp) //check if the date is correct
{
	const regex pattern("\\d{2,4}[-\.] \\d{0,2}[-\.] \\d{0,2}");
	return regex_match(temp,pattern);
}


家系図には詳細な生年月日を持たない人が多いことを考えると、やはりここでの規則性はあまり高くないようだ。

  • 一般的な入力の検出
    intデータが欲しいのに、ユーザーがcharを入力した場合、それを処理しなければ当然エラーになります。
    そこで、テストを行う必要があり、コードは以下のようになります(Detect関数をオーバーロードしています)。
void Detect(bool *temp)
{
	while(! (cin>>*temp))
	{
		cin.clear(); //clear cin
		//clear the buffer of illegitimate characters
		while(cin.get()! ='\n'){
			continue;
		}
		cout<<"prompt: input error \n please re-enter: "<<<endl;
	}
}

void Detect(int *temp)
{
	while(! (cin>>*temp)) 
	{
		cin.clear();
		while(cin.get()! ='\n'){
			continue;
		}
		cout<<"prompt: input error \n please re-enter: "<<<endl;
	}
}

void Detect(double *temp)
{
	while(! (cin>>*temp))
	{
		cin.clear();
		while(cin.get()! ='\n'){
			continue;
		}
		cout<<"prompt: input error \n please re-enter: "<<<endl;
	}
}


5. いくつかのブレーンストーミング

当初は、ユーザーデータ(アカウントパスワード)をデータベースに保存し、ログインして入力が正しいかどうかを確認するというものでしたが、何らかの理由でそれが行われませんでした。また、クローラーを使ってウェブを巡回し、既成の系図をいくつかデータベースに格納し、ユーザーが自分で選んでその系図を見るという案もありましたが、ウェブ上に既成のものはなく、あきらめるしかなかったので、データベースは使わず、ファイルを使ってデータを格納することにしました。でも、ずっと遊びたかったことを思いついたんです ----- 顔認証によるロック解除。

作業準備中
opencv+numpyのインストール

参考
opencv+numpyのインストールと設定
インストールも少し失敗しました(opencvとnumpyのバージョンに互換性がない)。

解決策
http://blog.csdn.net/tchchan/article/details/76177505
http://blog.csdn.net/baobao3456810/article/details/52177316

具体的な実装方法 ここにリンクの内容を書き込む

githubのアドレスです。 系図管理システム
ソースコードのダウンロードはこちら ここにリンクの内容を記述する