项目 - 文档快速搜索工具

一.项目简介:

Linux操作系统中提供了find指令查找指定文件,为了实现在Windows操作系统中实现文件的快搜搜索,利用所学知识实现文件搜索工具,与软件everything工具具有一定的相同功能,即:快速通过汉字,首字母,拼音实现文件的快速搜索。

二.项目需求及环境

(一)需求
通过汉字,拼音,拼音首字母实现文件快速搜索;
实现搜索关键字的高亮处理。
(二)开发环境
编译器 : VS2013 / 控制应用平台
编程语言 : C++ / C++11
数据库 : sqlite3
sqlite3菜鸟教程:https://www.runoob.com/sqlite/sqlite-tutorial.html
(三)涉及知识点

  1. 数据库操作:(sqlite安装,创建数据库,创建表,插入数据,删除数据,创建索引,查询数据 (条件查询、 模糊查询));
  2. 静态库和动态库:静态库和动态库的制作,动态库和动态的使用。
  3. 设计模式(单例模式);
  4. 多线程;
  5. 同步机制(互斥量);
  6. 日志;
  7. 汉字与拼音的转换。

三.项目设计

(一)设计框架
设计框架
(二)代码框架

  1. 公共模块 : Common.h
  2. 系统工具模块 : Sysutil.h Sysutil.cpp
  3. 数据管理模块 :DataManager.h DataManager.cpp
  4. 扫描管理模块 : ScanManager.h ScanManager.cpp
  5. 系统驱动模块 : DocFastSearchTool.cpp

(三)文件扫描
文件系统监控是利用系统文件系统的接口可以监控某个目录下的文档变化,有点是效率高实时性强,缺点是监控是实时的,如果在监控程序没有启动期间的,文档的变化无法获取。
文件系统扫描是通过系统接口,遍历获取目录下的所有文档跟数据库中的文档进行对比,获取文档变化,优点是监控程序启动前,变化的文档也能对比出来,缺点是性能低实时性不强。
(四)数据持久化
数据持久化我们使用了轻量级的一个数据库sqlite管理,使用sqlite3创建一张表。
(五)中间逻辑层

  1. 模糊匹配
    利用数据库中like语句进行模糊匹配搜索。
  2. 拼音全拼搜索
    存储时将文件名转换成一个拼音全拼存在数据库表的doc_name_pinyin字段中,搜索时也将关键字转换成拼音,然后使用数据库的模糊匹配搜索。
  3. 拼音首字母搜索
    存储时将文件名转换成一个拼音首字母存在数据库表的doc_name_initials字段中,搜索时也将关键字转换成拼音首字母,然后使用数据库的模糊匹配搜索。
  4. 高亮处理
    高亮处理需要对搜索出的关键字高亮标记处理,如果是直接的模糊匹配,比较简单,就是一个子串匹配,但是拼音全拼搜索和首字母搜索需要使用一套逻辑算法来处理。

四.项目实现

(一)系统工具模块的实现:

Sysutil.h文件
//界面模块

#define WIDTH 120
#define HEIGHT 30

void SetCurPos(int x, int y); //x 带表界面的行   y 代表界面的列
void HideCursor();

void DrawCol(int x, int y);
void DrawRow(int x, int y);

void DrawFrame(char *title);
void DrawMenu();

void SystemEnd();

/
//获取文件个数
size_t GetFileCount(const string &path);

//目录监控函数
bool DirectoryWatch(const string &path);

//系统功能函数模块
void DirectoryList(const string &path, vector<string> &subfile, vector<string> &subdir);

// 汉字转拼音全拼
/* CSDN:http://blog.csdn.net/csnd_ayo */
string ChineseConvertPinYinAllSpell(const string& dest_chinese);
// 汉字转拼音首字母
string ChineseConvertPinYinInitials(const string& name);

// 颜色高亮显示一段字符串
void ColourPrintf(const string  &str);

目录扫描

//目录扫描
extern size_t g_FileCount;
extern size_t g_ScanCount;

size_t GetFileCount(const string &path)
{
	string _path = path + "\\" + "*.*";
	struct _finddata_t fileAttri;
	long handle = _findfirst(_path.c_str(), &fileAttri);
	if(handle == -1)
		return 0;

	do
	{
		if(fileAttri.name[0] == '.')
			continue;
		g_ScanCount++;

		if(fileAttri.attrib & _A_SUBDIR)
			GetFileCount(path + "\\" + fileAttri.name);

	}while(_findnext(handle, &fileAttri) == 0);
	return g_ScanCount;
}

bool DirectoryWatch(const string &path)
{
	size_t file_count = GetFileCount(path);
	return file_count != g_FileCount;
}


void DirectoryList(const string &path, vector<string> &subfile, vector<string> &subdir)
{
	//"E:\\Users\\Documents\\计算机专业课件\\*.*";
	string _path = path;
	_path += "\\*.*";  //通配符  * ?%

	struct _finddata_t file;

	long handle = _findfirst(_path.c_str(), &file);
	if(handle == -1)
	{
		perror("_findfirst");
		return;
	}

	do
	{
		if(file.name[0] == '.')
			continue;

		//_A_SUBDIR
		//是一个文件夹
		if(file.attrib & _A_SUBDIR)     
			subdir.push_back(file.name);
		else
			subfile.push_back(file.name);
	}while(_findnext(handle, &file) == 0);

界面模块

#include"Sysutil.h"

//设置光标位置
void SetCurPos(int x, int y)
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = {y, x};
	SetConsoleCursorPosition(handle, pos);
}
//隐藏光标
void HideCursor()
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO cursor_info = {100, 0};
	SetConsoleCursorInfo(handle, &cursor_info);
}
//画列  |
void DrawCol(int x, int y)
{
	for(int i=0; i<HEIGHT; ++i)
	{
		SetCurPos(x+i, y);
		printf("|");
	}
}

//画行 -
void DrawRow(int x, int y)
{
	for(int i=0; i<WIDTH-4; ++i)
	{
		SetCurPos(x, y+i);
		printf("—");
	}
}

void SystemEnd()
{
	SetCurPos(HEIGHT-1, (WIDTH-4-strlen("请按任意键继续 . . ."))/2);
}

//画系统框架界面
void DrawFrame(char *title)
{
	//设置标题
	char buf[MAX_BUF_SIZE] = {0};
	sprintf(buf, "title %s", title);
	system(buf);

	//设置界面的宽度和高度
	memset(buf, 0, MAX_BUF_SIZE);
	sprintf(buf, "mode con cols=%d lines=%d", WIDTH, HEIGHT);
	system(buf);

	//设置颜色

	//设置系统界面
	DrawCol(0, 0);  //左边列
	DrawCol(0, WIDTH-2); //右边列
	DrawRow(0, 2);
	DrawRow(2, 2);
	DrawRow(4, 2);

	DrawRow(HEIGHT-6, 2);
	DrawRow(HEIGHT-4, 2);
	DrawRow(HEIGHT-2, 2);
}

extern char *title;
void DrawMenu()
{
	//设置标题
	SetCurPos(1, (WIDTH-4-strlen(title))/2);
	printf("%s", title);

	//设置名称 路径
	SetCurPos(3, 2);
	printf("%-30s%-85s", "名称", "路径");

	//设置 exit 退出系统
	SetCurPos(HEIGHT-3, (WIDTH-4-strlen("exit 退出系统 ."))/2);
	printf("%s","exit 退出系统 .");

	//设置 输入:>
	SetCurPos(HEIGHT-5, 2);
	printf("%s","请输入:");
}

日志模块

//日志模块
#ifndef __TRACE__
	//#define __TRACE__
#endif

#ifndef __DEBUG__
	//#define __DEBUG__
#endif

///
static std::string GetFileName(const std::string& path)
{
	char ch = '/';  //Linux   

#ifdef _WIN32      
	ch = '\\'; 
#endif 
	size_t pos = path.rfind(ch);
	if (pos == std::string::npos)
		return path;
	else
		return path.substr(pos + 1);
}

//用于调试追溯的trace log
inline static void __TraceDebug(const char* filename, int line, const char* function, const char* format, ...)
{
#ifdef __TRACE__
	//输出调用函数的信息
	fprintf(stdout,"[TRACE][%s:%d:%s]:",GetFileName(filename).c_str(), line, function);

	//可变参数
	//输出用户打的trace信息
	va_list args;
	va_start(args,format);
	vfprintf(stdout,format, args);
	va_end(args);

	fprintf(stdout,"\n");
#endif
}

inline static void __ErrorDebug(const char* filename, int line, const char* function, const char* format, ...)
{
#ifdef __DEBUG__
	//输出调用函数的信息
	fprintf(stdout,"[ERROR][%s:%d:%s]:",GetFileName(filename).c_str(), line, function);

	//输出用户打的trace信息
	va_list args;
	va_start(args,format);
	vfprintf(stdout,format, args);
	va_end(args);

	fprintf(stdout," errmsg:%s, errno:%d\n", strerror(errno), errno);
#endif
}

#define TRACE_LOG(...) \
	__TraceDebug(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__);

#define ERROR_LOG(...) \
	__ErrorDebug(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__);

汉字转拼音全拼

// 汉字转拼音全拼
/* CSDN:http://blog.csdn.net/csnd_ayo */
string ChineseConvertPinYinAllSpell(const string& dest_chinese)
{
	static const int spell_value[] =
	{
		-20319, -20317, -20304, -20295, -20292, -20283, -20265, -20257, -20242, -20230, -20051, -20036, -20032, -20026,
		-20002, -19990, -19986, -19982, -19976, -19805, -19784, -19775, -19774, -19763, -19756, -19751, -19746, -19741, -19739, -19728,
		-19725, -19715, -19540, -19531, -19525, -19515, -19500, -19484, -19479, -19467, -19289, -19288, -19281, -19275, -19270, -19263,
		-19261, -19249, -19243, -19242, -19238, -19235, -19227, -19224, -19218, -19212, -19038, -19023, -19018, -19006, -19003, -18996,
		-18977, -18961, -18952, -18783, -18774, -18773, -18763, -18756, -18741, -18735, -18731, -18722, -18710, -18697, -18696, -18526,
		-18518, -18501, -18490, -18478, -18463, -18448, -18447, -18446, -18239, -18237, -18231, -18220, -18211, -18201, -18184, -18183,
		-18181, -18012, -17997, -17988, -17970, -17964, -17961, -17950, -17947, -17931, -17928, -17922, -17759, -17752, -17733, -17730,
		-17721, -17703, -17701, -17697, -17692, -17683, -17676, -17496, -17487, -17482, -17468, -17454, -17433, -17427, -17417, -17202,
		-17185, -16983, -16970, -16942, -16915, -16733, -16708, -16706, -16689, -16664, -16657, -16647, -16474, -16470, -16465, -16459,
		-16452, -16448, -16433, -16429, -16427, -16423, -16419, -16412, -16407, -16403, -16401, -16393, -16220, -16216, -16212, -16205,
		-16202, -16187, -16180, -16171, -16169, -16158, -16155, -15959, -15958, -15944, -15933, -15920, -15915, -15903, -15889, -15878,
		-15707, -15701, -15681, -15667, -15661, -15659, -15652, -15640, -15631, -15625, -15454, -15448, -15436, -15435, -15419, -15416,
		-15408, -15394, -15385, -15377, -15375, -15369, -15363, -15362, -15183, -15180, -15165, -15158, -15153, -15150, -15149, -15144,
		-15143, -15141, -15140, -15139, -15128, -15121, -15119, -15117, -15110, -15109, -14941, -14937, -14933, -14930, -14929, -14928,
		-14926, -14922, -14921, -14914, -14908, -14902, -14894, -14889, -14882, -14873, -14871, -14857, -14678, -14674, -14670, -14668,
		-14663, -14654, -14645, -14630, -14594, -14429, -14407, -14399, -14384, -14379, -14368, -14355, -14353, -14345, -14170, -14159,
		-14151, -14149, -14145, -14140, -14137, -14135, -14125, -14123, -14122, -14112, -14109, -14099, -14097, -14094, -14092, -14090,
		-14087, -14083, -13917, -13914, -13910, -13907, -13906, -13905, -13896, -13894, -13878, -13870, -13859, -13847, -13831, -13658,
		-13611, -13601, -13406, -13404, -13400, -13398, -13395, -13391, -13387, -13383, -13367, -13359, -13356, -13343, -13340, -13329,
		-13326, -13318, -13147, -13138, -13120, -13107, -13096, -13095, -13091, -13076, -13068, -13063, -13060, -12888, -12875, -12871,
		-12860, -12858, -12852, -12849, -12838, -12831, -12829, -12812, -12802, -12607, -12597, -12594, -12585, -12556, -12359, -12346,
		-12320, -12300, -12120, -12099, -12089, -12074, -12067, -12058, -12039, -11867, -11861, -11847, -11831, -11798, -11781, -11604,
		-11589, -11536, -11358, -11340, -11339, -11324, -11303, -11097, -11077, -11067, -11055, -11052, -11045, -11041, -11038, -11024,
		-11020, -11019, -11018, -11014, -10838, -10832, -10815, -10800, -10790, -10780, -10764, -10587, -10544, -10533, -10519, -10331,
		-10329, -10328, -10322, -10315, -10309, -10307, -10296, -10281, -10274, -10270, -10262, -10260, -10256, -10254 
	};

	// 395个字符串,每个字符串长度不超过6
	static const char spell_dict[396][7] =
	{
		"a", "ai", "an", "ang", "ao", "ba", "bai", "ban", "bang", "bao", "bei", "ben", "beng", "bi", "bian", "biao",
		"bie", "bin", "bing", "bo", "bu", "ca", "cai", "can", "cang", "cao", "ce", "ceng", "cha", "chai", "chan", "chang", "chao", "che", "chen",
		"cheng", "chi", "chong", "chou", "chu", "chuai", "chuan", "chuang", "chui", "chun", "chuo", "ci", "cong", "cou", "cu", "cuan", "cui",
		"cun", "cuo", "da", "dai", "dan", "dang", "dao", "de", "deng", "di", "dian", "diao", "die", "ding", "diu", "dong", "dou", "du", "duan",
		"dui", "dun", "duo", "e", "en", "er", "fa", "fan", "fang", "fei", "fen", "feng", "fo", "fou", "fu", "ga", "gai", "gan", "gang", "gao",
		"ge", "gei", "gen", "geng", "gong", "gou", "gu", "gua", "guai", "guan", "guang", "gui", "gun", "guo", "ha", "hai", "han", "hang",
		"hao", "he", "hei", "hen", "heng", "hong", "hou", "hu", "hua", "huai", "huan", "huang", "hui", "hun", "huo", "ji", "jia", "jian",
		"jiang", "jiao", "jie", "jin", "jing", "jiong", "jiu", "ju", "juan", "jue", "jun", "ka", "kai", "kan", "kang", "kao", "ke", "ken",
		"keng", "kong", "kou", "ku", "kua", "kuai", "kuan", "kuang", "kui", "kun", "kuo", "la", "lai", "lan", "lang", "lao", "le", "lei",
		"leng", "li", "lia", "lian", "liang", "liao", "lie", "lin", "ling", "liu", "long", "lou", "lu", "lv", "luan", "lue", "lun", "luo",
		"ma", "mai", "man", "mang", "mao", "me", "mei", "men", "meng", "mi", "mian", "miao", "mie", "min", "ming", "miu", "mo", "mou", "mu",
		"na", "nai", "nan", "nang", "nao", "ne", "nei", "nen", "neng", "ni", "nian", "niang", "niao", "nie", "nin", "ning", "niu", "nong",
		"nu", "nv", "nuan", "nue", "nuo", "o", "ou", "pa", "pai", "pan", "pang", "pao", "pei", "pen", "peng", "pi", "pian", "piao", "pie",
		"pin", "ping", "po", "pu", "qi", "qia", "qian", "qiang", "qiao", "qie", "qin", "qing", "qiong", "qiu", "qu", "quan", "que", "qun",
		"ran", "rang", "rao", "re", "ren", "reng", "ri", "rong", "rou", "ru", "ruan", "rui", "run", "ruo", "sa", "sai", "san", "sang",
		"sao", "se", "sen", "seng", "sha", "shai", "shan", "shang", "shao", "she", "shen", "sheng", "shi", "shou", "shu", "shua",
		"shuai", "shuan", "shuang", "shui", "shun", "shuo", "si", "song", "sou", "su", "suan", "sui", "sun", "suo", "ta", "tai",
		"tan", "tang", "tao", "te", "teng", "ti", "tian", "tiao", "tie", "ting", "tong", "tou", "tu", "tuan", "tui", "tun", "tuo",
		"wa", "wai", "wan", "wang", "wei", "wen", "weng", "wo", "wu", "xi", "xia", "xian", "xiang", "xiao", "xie", "xin", "xing",
		"xiong", "xiu", "xu", "xuan", "xue", "xun", "ya", "yan", "yang", "yao", "ye", "yi", "yin", "ying", "yo", "yong", "you",
		"yu", "yuan", "yue", "yun", "za", "zai", "zan", "zang", "zao", "ze", "zei", "zen", "zeng", "zha", "zhai", "zhan", "zhang",
		"zhao", "zhe", "zhen", "zheng", "zhi", "zhong", "zhou", "zhu", "zhua", "zhuai", "zhuan", "zhuang", "zhui", "zhun", "zhuo",
		"zi", "zong", "zou", "zu", "zuan", "zui", "zun", "zuo" 
	};
	std::string pinyin;
	// 循环处理字节数组
	const int length = dest_chinese.length();
	for (int j = 0, chrasc = 0; j < length;)
	{
		// 非汉字处理
		if (dest_chinese.at(j) >= 0 && dest_chinese.at(j) < 128)
		{
			pinyin += dest_chinese[j];
			// 偏移下标
			j++;
			continue;
		}
		// 汉字处理
		chrasc = dest_chinese[j] * 256 + dest_chinese[j + 1] + 256;
		if (chrasc > 0 && chrasc < 160)
		{
			// 非汉字
			pinyin += dest_chinese.at(j);
			// 偏移下标
			j++;
		}
		else
		{
			// 汉字
			for (int i = (sizeof(spell_value) / sizeof(spell_value[0]) - 1); i >= 0; --i)
			{
				// 查找字典
				if (spell_value[i] <= chrasc)
				{
					pinyin += spell_dict[i];
					break;
				}
			}
			// 偏移下标 (汉字双字节)
			j += 2;
		}
	} // for end
	return pinyin;
}

汉字转拼音首字母

string ChineseConvertPinYinInitials(const string& name)
{
	// 仅生成拼音首字母内容
	static int secPosValue[] =
	{
		1601, 1637, 1833, 2078, 2274, 2302, 2433, 2594, 2787, 3106, 3212,
		3472, 3635, 3722, 3730, 3858, 4027, 4086, 4390, 4558, 4684, 4925, 5249
	};

	static const char* initials[] =
	{
		"a", "b", "c", "d", "e", "f", "g", "h", "j", "k", "l", "m", "n", "o",
		"p", "q", "r", "s", "t", "w", "x", "y", "z"
	};

	static const char* secondSecTable =
		"CJWGNSPGCGNE[Y[BTYYZDXYKYGT[JNNJQMBSGZSCYJSYY[PGKBZGY[YWJKGKLJYWKPJQHY[W[DZLSGMRYPYWWCCKZNKYYGTTNJJNYKKZYTCJNMCYLQLYPYQFQRPZSLWBTGKJFYXJWZLTBNCXJJJJTXDTTSQZYCDXXHGCK[PHFFSS[YBGXLPPBYLL[HLXS[ZM[JHSOJNGHDZQYKLGJHSGQZHXQGKEZZWYSCSCJXYEYXADZPMDSSMZJZQJYZC[J[WQJBYZPXGZNZCPWHKXHQKMWFBPBYDTJZZKQHY"
		"LYGXFPTYJYYZPSZLFCHMQSHGMXXSXJ[[DCSBBQBEFSJYHXWGZKPYLQBGLDLCCTNMAYDDKSSNGYCSGXLYZAYBNPTSDKDYLHGYMYLCXPY[JNDQJWXQXFYYFJLEJPZRXCCQWQQSBNKYMGPLBMJRQCFLNYMYQMSQYRBCJTHZTQFRXQHXMJJCJLXQGJMSHZKBSWYEMYLTXFSYDSWLYCJQXSJNQBSCTYHBFTDCYZDJWYGHQFRXWCKQKXEBPTLPXJZSRMEBWHJLBJSLYYSMDXLCLQKXLHXJRZJMFQHXHWY"
		"WSBHTRXXGLHQHFNM[YKLDYXZPYLGG[MTCFPAJJZYLJTYANJGBJPLQGDZYQYAXBKYSECJSZNSLYZHSXLZCGHPXZHZNYTDSBCJKDLZAYFMYDLEBBGQYZKXGLDNDNYSKJSHDLYXBCGHXYPKDJMMZNGMMCLGWZSZXZJFZNMLZZTHCSYDBDLLSCDDNLKJYKJSYCJLKWHQASDKNHCSGANHDAASHTCPLCPQYBSDMPJLPZJOQLCDHJJYSPRCHN[NNLHLYYQYHWZPTCZGWWMZFFJQQQQYXACLBHKDJXDGMMY"
		"DJXZLLSYGXGKJRYWZWYCLZMSSJZLDBYD[FCXYHLXCHYZJQ[[QAGMNYXPFRKSSBJLYXYSYGLNSCMHZWWMNZJJLXXHCHSY[[TTXRYCYXBYHCSMXJSZNPWGPXXTAYBGAJCXLY[DCCWZOCWKCCSBNHCPDYZNFCYYTYCKXKYBSQKKYTQQXFCWCHCYKELZQBSQYJQCCLMTHSYWHMKTLKJLYCXWHEQQHTQH[PQ[QSCFYMNDMGBWHWLGSLLYSDLMLXPTHMJHWLJZYHZJXHTXJLHXRSWLWZJCBXMHZQXSDZP"
		"MGFCSGLSXYMJSHXPJXWMYQKSMYPLRTHBXFTPMHYXLCHLHLZYLXGSSSSTCLSLDCLRPBHZHXYYFHB[GDMYCNQQWLQHJJ[YWJZYEJJDHPBLQXTQKWHLCHQXAGTLXLJXMSL[HTZKZJECXJCJNMFBY[SFYWYBJZGNYSDZSQYRSLJPCLPWXSDWEJBJCBCNAYTWGMPAPCLYQPCLZXSBNMSGGFNZJJBZSFZYNDXHPLQKZCZWALSBCCJX[YZGWKYPSGXFZFCDKHJGXDLQFSGDSLQWZKXTMHSBGZMJZRGLYJB"
		"PMLMSXLZJQQHZYJCZYDJWBMYKLDDPMJEGXYHYLXHLQYQHKYCWCJMYYXNATJHYCCXZPCQLBZWWYTWBQCMLPMYRJCCCXFPZNZZLJPLXXYZTZLGDLDCKLYRZZGQTGJHHGJLJAXFGFJZSLCFDQZLCLGJDJCSNZLLJPJQDCCLCJXMYZFTSXGCGSBRZXJQQCTZHGYQTJQQLZXJYLYLBCYAMCSTYLPDJBYREGKLZYZHLYSZQLZNWCZCLLWJQJJJKDGJZOLBBZPPGLGHTGZXYGHZMYCNQSYCYHBHGXKAMTX"
		"YXNBSKYZZGJZLQJDFCJXDYGJQJJPMGWGJJJPKQSBGBMMCJSSCLPQPDXCDYYKY[CJDDYYGYWRHJRTGZNYQLDKLJSZZGZQZJGDYKSHPZMTLCPWNJAFYZDJCNMWESCYGLBTZCGMSSLLYXQSXSBSJSBBSGGHFJLYPMZJNLYYWDQSHZXTYYWHMZYHYWDBXBTLMSYYYFSXJC[DXXLHJHF[SXZQHFZMZCZTQCXZXRTTDJHNNYZQQMNQDMMG[YDXMJGDHCDYZBFFALLZTDLTFXMXQZDNGWQDBDCZJDXBZGS"
		"QQDDJCMBKZFFXMKDMDSYYSZCMLJDSYNSBRSKMKMPCKLGDBQTFZSWTFGGLYPLLJZHGJ[GYPZLTCSMCNBTJBQFKTHBYZGKPBBYMTDSSXTBNPDKLEYCJNYDDYKZDDHQHSDZSCTARLLTKZLGECLLKJLQJAQNBDKKGHPJTZQKSECSHALQFMMGJNLYJBBTMLYZXDCJPLDLPCQDHZYCBZSCZBZMSLJFLKRZJSNFRGJHXPDHYJYBZGDLQCSEZGXLBLGYXTWMABCHECMWYJYZLLJJYHLG[DJLSLYGKDZPZXJ"
		"YYZLWCXSZFGWYYDLYHCLJSCMBJHBLYZLYCBLYDPDQYSXQZBYTDKYXJY[CNRJMPDJGKLCLJBCTBJDDBBLBLCZQRPPXJCJLZCSHLTOLJNMDDDLNGKAQHQHJGYKHEZNMSHRP[QQJCHGMFPRXHJGDYCHGHLYRZQLCYQJNZSQTKQJYMSZSWLCFQQQXYFGGYPTQWLMCRNFKKFSYYLQBMQAMMMYXCTPSHCPTXXZZSMPHPSHMCLMLDQFYQXSZYYDYJZZHQPDSZGLSTJBCKBXYQZJSGPSXQZQZRQTBDKYXZK"
		"HHGFLBCSMDLDGDZDBLZYYCXNNCSYBZBFGLZZXSWMSCCMQNJQSBDQSJTXXMBLTXZCLZSHZCXRQJGJYLXZFJPHYMZQQYDFQJJLZZNZJCDGZYGCTXMZYSCTLKPHTXHTLBJXJLXSCDQXCBBTJFQZFSLTJBTKQBXXJJLJCHCZDBZJDCZJDCPRNPQCJPFCZLCLZXZDMXMPHJSGZGSZZQLYLWTJPFSYASMCJBTZKYCWMYTCSJJLJCQLWZMALBXYFBPNLSFHTGJWEJJXXGLLJSTGSHJQLZFKCGNNNSZFDEQ"
		"FHBSAQTGYLBXMMYGSZLDYDQMJJRGBJTKGDHGKBLQKBDMBYLXWCXYTTYBKMRTJZXQJBHLMHMJJZMQASLDCYXYQDLQCAFYWYXQHZ";
	const char* cName = name.c_str();
	std::string result;
	int H = 0, L = 0, W = 0, j = 0;
	size_t stringlen = ::strlen(cName);
	for (size_t i = 0; i < stringlen; ++i)
	{
		H = (unsigned char)(cName[i + 0]);
		L = (unsigned char)(cName[i + 1]);
		if (H < 0xA1 || L < 0xA1)
		{
			result += cName[i];
			continue;
		}
		W = (H - 160) * 100 + L - 160;
		if (W > 1600 && W < 5590)
		{
			bool has = false;
			for (j = 22; j >= 0; j--)
			{
				if (W >= secPosValue[j])
				{
					result += initials[j];
					i++;
					has = true;
					break;
				}
			}
			continue;
		}
		i++;
		W = (H - 160 - 56) * 94 + L - 161;
		if (W >= 0 && W <= 3007)
			result += secondSecTable[W];
		else
		{
			result += (unsigned char)H;
			result += (unsigned char)L;
		}
	}
	return result;
}

高亮处理

void ColourPrintf(const string  &str)
{
	// 0-黑 1-蓝 2-绿 3-浅绿 4-红 5-紫 6-黄 7-白 8-灰 9-淡蓝 10-淡绿
	// 11-淡浅绿  12-淡红 13-淡紫 14-淡黄 15-亮白 
	//颜色:前景色 + 背景色*0x10 
	//例如:字是红色,背景色是白色,即 红色 + 亮白 =  4 + 15*0x10 
	WORD color = 11 + 0 * 0x10;
	WORD colorOld;
	HANDLE handle = ::GetStdHandle(STD_OUTPUT_HANDLE);

	CONSOLE_SCREEN_BUFFER_INFO csbi;
	GetConsoleScreenBufferInfo(handle, &csbi);   //白色
	colorOld = csbi.wAttributes;

	SetConsoleTextAttribute(handle, color);      //红色
	
	printf("%s", str.c_str());
	SetConsoleTextAttribute(handle, colorOld);   //白色
}

(二)数据库管理模块
整个工具在搜索文档的过程中,其扫描实例只需要一个,这个扫描实例只需要把需要扫描的路径下面的文档(包括子目录)全部扫进数据库即可,不需要多个扫描对象,如果有多个可能会造成数据库数据重复,或者产生脏数据,从而导致搜索工具的不准确。
扫描类封装(ScanManager.h ):

#pragma once

#include"Common.h"
#include"Sysutil.h"
#include"DataManager.h"

//单件模式:
class ScanManager
{
public:
	static ScanManager& CreateInstance(const string &path);
public:
	void StartScan(const string &path);
	void StartWatch(const string &path);
	void ScanDirectory(const string &path);
private:
	ScanManager();
	//DataManager m_db;
	mutex m_mutex;   //互斥量
	condition_variable m_cond;  //条件变量
};

类的实现(ScanManager.cpp):

#include"ScanManager.h"

size_t g_FileCount = 0;
size_t g_ScanCount = 0;

ScanManager::ScanManager()
{}

void ScanManager::StartScan(const string &path)
{
	while(1)
	{
		//this_thread::sleep_for(chrono::seconds(2)); //

		unique_lock<mutex> lock(m_mutex);
		m_cond.wait(lock);

		ScanDirectory(path);
	}
}
void ScanManager::StartWatch(const string &path)
{
	//增加重命名监控
	g_FileCount = GetFileCount(path);
	while(1)
	{
		//监控path目录的文件个数是否有变化
		g_ScanCount = 0;
		bool ischange = DirectoryWatch(path);
		if(ischange)
		{
			//发送扫描信号
			m_cond.notify_one();
			g_FileCount = g_ScanCount;
		}
		this_thread::sleep_for(chrono::seconds(3));
	}
}

ScanManager& ScanManager::CreateInstance(const string &path)
{
	static ScanManager inst;
	//创建扫描线程
//整个搜索过程中,有可能本地文档会有增加或删除的情况,
//如果扫描实例只扫描一次就停止了扫描,会导致本地数据与数据库的数据不对称,
//因此,需要时刻扫描本地数据,从而达到对数据库文件的实时更新(本地的实时理论上也是有一定的时间间隔,或称为延迟),
//一旦线程化,我们可以通过子线程来完成扫描工作,而主线程继续完成与用户的交互,因此,需要线程化。
	thread scan_thread(&StartScan, &inst, path);
	scan_thread.detach();

	//创建监控线程
	thread watch_thread(&StartWatch, &inst, path);
	watch_thread.detach();
	return inst;
}

void ScanManager::ScanDirectory(const string &path)
{
	//1 扫描本地文件 并进行存储
	vector<string> local_files;
	vector<string> local_dirs;
	DirectoryList(path, local_files, local_dirs);
	set<string> local_set;
	local_set.insert(local_files.begin(), local_files.end());
	local_set.insert(local_dirs.begin(), local_dirs.end());

	DataManager &m_db = DataManager::CreateInstance();

	//2 扫描数据库文件 并进行存储
	set<string> db_set;
	//查询数据数据库
	m_db.GetDocs(path, db_set);

	//3 进行文件对比
	auto local_it = local_set.begin();
	auto db_it = db_set.begin();
	while(local_it!=local_set.end() && db_it!=db_set.end())
	{
		if(*local_it < *db_it)
		{
			//本地文件存在,数据库文件不存在,则数据库增加文件
			//增加文件
			m_db.InsertDoc(path, *local_it);
			++local_it;
		}
		else if(*local_it > *db_it)
		{
			//本地文件不存在,数据库文件存在,则数据库删除文件
			//删除文件
			m_db.DeleteDoc(path, *db_it);
			++db_it;
		}
		else
		{
			//本地文件存在,数据库文件也存在,则数据库不变
			++local_it;
			++db_it;
		}
	}

	//本地文件存在, 增加数据库文件
	while(local_it != local_set.end())
	{
		//增加文件
		m_db.InsertDoc(path, *local_it);
		++local_it;
	}

	//数据库文件存在, 删除数据库文件
	while(db_it != db_set.end())
	{
		//删除文件
		m_db.DeleteDoc(path, *db_it);
		++db_it;
	}

	//递归遍历子目录
	for(auto &dir : local_dirs)
	{
		//E:\\Users\\Documents\\计算机专业课件
		string dir_path = path;
		dir_path += '\\';
		dir_path += dir;

		ScanDirectory(dir_path);
	}
}

(三)数据管理模块的实现
数据库sqlite3封装

class SqliteManager
{
public :
	SqliteManager();
	~SqliteManager();
public:
	//打开数据库
	void Open(const string& path);
	//关闭数据库
	void Close();
	//执行sql语句
	void ExecuteSql(const string& sql);
	//获取表结果
	void GetResTable(const string& sql, int& row, int& col, char**&ppRet);
public:
	//c++11不允许数据库对象进行拷贝构造
	SqliteManager(const SqliteManager&) = delete; 
	//不允许数据库对象进行赋值
	SqliteManager& operator=(const SqliteManager&) = delete; 
private:
	sqlite3* m_db; // 数据库对象
};

实现:

SqliteManager::SqliteManager() :m_db(nullptr)
{}
SqliteManager::~SqliteManager()
{
	Close();
}
void SqliteManager::Open(const string &path)
{
	int rc = sqlite3_open(path.c_str(), &m_db);
	if(rc != SQLITE_OK)
	{
		ERROR_LOG("Can't open database");
		return;
	}
	else
		TRACE_LOG("Opened database successfully")
}

void SqliteManager::Close()
{
	if(m_db)
	{
		int rc = sqlite3_close(m_db);
		if(rc != SQLITE_OK)
		{
			ERROR_LOG("Close Database failed.");
			return;
		}
		else
			TRACE_LOG("Close Database successfully.");
		m_db = nullptr;
	}
}

void SqliteManager::ExecuteSql(const string &sql)
{
	char *zErrMsg = 0;
	int rc = sqlite3_exec(m_db, sql.c_str(), 0, 0, &zErrMsg);
	if (rc != SQLITE_OK)
	{
		ERROR_LOG("SQL error(sql: %s): %s\n",sql.c_str(), zErrMsg);
		sqlite3_free(zErrMsg);
	}
	else
	{
		TRACE_LOG("Exec Sql(sql:%s) successfully\n", sql.c_str());
	}
}

void SqliteManager::GetResTable(const string& sql, int &row, int &col, char**&ppRet)
{
	char *zErrMsg = 0;
	int rc = sqlite3_get_table(m_db, sql.c_str(), &ppRet, &row, &col, &zErrMsg);

	if (rc != SQLITE_OK)
	{
		ERROR_LOG("GetResTable error(sql: %s): %s\n",sql.c_str(), zErrMsg);
		sqlite3_free(zErrMsg);
	}
	else
	{
		TRACE_LOG("GetResTable  successfully");
	}
}

自动获取表结果类

class AutoGetResTable
{
public:
	AutoGetResTable(SqliteManager *db, const string &sql, int &row, int &col, char **&ppRet);
	~AutoGetResTable();
public:
	AutoGetResTable(const AutoGetResTable &) = delete;
	AutoGetResTable& operator=(const AutoGetResTable &) = delete;
private:
	SqliteManager *m_db;
	char         **m_ppRet;
};

实现:

AutoGetResTable::AutoGetResTable(SqliteManager *db, const string &sql, int &row, int &col, char **&ppRet)
:m_db(db),m_ppRet(nullptr)
{
	m_db->GetResTable(sql, row, col, ppRet);
	m_ppRet = ppRet;

}
AutoGetResTable::~AutoGetResTable()
{
	if(m_ppRet)
		sqlite3_free_table(m_ppRet);
}

数据库管理

#define DOC_DB     "doc.db"
#define DOC_TABLE  "doc_tb"

//数据库管理模块
class DataManager
{
public:
	static DataManager& CreateInstance();
public:
	~DataManager();
public:
	void InitSqlite();
public:
	//向数据库获取数据
	void GetDocs(const string &path, set<string>& docs);
	//向数据库插入文档
	void InsertDoc(const string &path, string doc);
	//向数据库删除文档
	void DeleteDoc(const string &path, string doc);
	//清空数据库
	void ClearDoc();
public:
	void Search(const string &key, vector<pair<string, string>> &doc_path);
	static void SplitHightLight(const string &str, const string &key, string &prefix, string &hightlight, string &suffix);
private:
	DataManager();
private:
	SqliteManager m_dbmgr;
};

实现:

DataManager& DataManager::CreateInstance()
{
	static DataManager inst;
	return inst;
}

DataManager::DataManager()
{
	m_dbmgr.Open(DOC_DB);
	
	//创建数据库表
	InitSqlite();

	//清空
	ClearDoc();
}
DataManager::~DataManager()
{}

void DataManager::InitSqlite()
{
	char sql[MAX_SQL_SIZE] = {0};
	sprintf(sql,  "create table if not exists %s (id INTEGER PRIMARY KEY autoincrement, doc_path text, doc_name text,doc_name_pinyin text, doc_name_initials text)", DOC_TABLE);
	m_dbmgr.ExecuteSql(sql);
}

//向数据库获取数据
void DataManager::GetDocs(const string &path, set<string>& docs)
{
	//select doc_name from doc_tb where doc_path = path.c_str()
	char sql[MAX_SQL_SIZE] = {0};
	sprintf(sql, "select doc_name from %s where doc_path='%s'", DOC_TABLE, path.c_str());

	int row=0, col=0;
	char **ppRet = 0;

	//获取结果表
	//m_dbmgr.GetResTable(sql, row, col, ppRet);
	AutoGetResTable at(&m_dbmgr, sql, row, col, ppRet);

    //将结果保存到set
	for(int i=1; i<=row; ++i)
		docs.insert(ppRet[i]);

	//释放结果表
	//sqlite3_free_table(ppRet);
}
//向数据库插入文档
void DataManager::InsertDoc(const string &path, string doc)
{
	char sql[MAX_SQL_SIZE] = {0};
	string pinyin = ChineseConvertPinYinAllSpell(doc);
	string initials = ChineseConvertPinYinInitials(doc);

	sprintf(sql, "insert into %s values(NULL, '%s', '%s', '%s', '%s')",
			DOC_TABLE, path.c_str(), doc.c_str(), pinyin.c_str(), initials.c_str());
	m_dbmgr.ExecuteSql(sql);
}

//向数据库删除文档
void DataManager::DeleteDoc(const string &path, string doc)
{
	char sql[MAX_SQL_SIZE] = {0};
	sprintf(sql, "delete from  %s where doc_path='%s' and doc_name='%s'", DOC_TABLE, path.c_str(), doc.c_str());
	m_dbmgr.ExecuteSql(sql);

	//递归删除子目录
	//E:\\Users\\Documents\\计算机专业课件
	string doc_path = path;
	doc_path += "\\";
	//E:\\Users\\Documents\\计算机专业课件
	doc_path += doc;
	//E:\\Users\\Documents\\计算机专业课件\AA
	sprintf(sql, "delete from %s where doc_path like '%s%%'", DOC_TABLE, doc_path.c_str());
	m_dbmgr.ExecuteSql(sql);
}

void DataManager::ClearDoc()
{
	char sql[MAX_SQL_SIZE] = {0};
	sprintf(sql, "delete from %s", DOC_TABLE);
	m_dbmgr.ExecuteSql(sql);
}

void DataManager::Search(const string &key, vector<pair<string, string>> &doc_path)
{
	char sql[MAX_SQL_SIZE] = {0};
	int row=0, col=0;
	char **ppRet = 0;
	
	//科技   %keji%
	string key_pinyin = "%";
	key_pinyin += ChineseConvertPinYinAllSpell(key);
	key_pinyin +="%";

	//科技   %kj%
	string key_initials = "%";
	key_initials += ChineseConvertPinYinInitials(key);
	key_initials += "%";

	//select dco_name, doc_path from doc_tb where doc_name like '%%s%';
	sprintf(sql, "select doc_name, doc_path from %s where doc_name_pinyin like '%s' or doc_name_initials like '%s'",
			DOC_TABLE, key_pinyin.c_str(), key_initials.c_str());

	//m_dbmgr.GetResTable(sql, row, col, ppRet);
	AutoGetResTable at(&m_dbmgr, sql, row, col, ppRet);


	for(int i=1; i<=row; ++i)
	{   										//name            path
		doc_path.push_back(make_pair(ppRet[i*col+0], ppRet[i*col+1]));
	}
	
	//sqlite3_free_table(ppRet);  //源代码级别
}

void DataManager::SplitHightLight(const string &str, const string &key, 
								  string &prefix, string &hightlight, string &suffix)
{

	string strlower = str;
	string keylower = key;
	transform(str.begin(), str.end(), strlower.begin(), tolower);
	transform(key.begin(), key.end(), keylower.begin(), tolower);

	//1 中文搜索, 如果匹配,直接分割
	size_t pos = strlower.find(keylower);
	if(pos != string::npos)
	{
		prefix = str.substr(0, pos);
		hightlight = str.substr(pos, keylower.size());
		suffix = str.substr(pos+keylower.size(), string::npos);
		return;
	}

	//2 拼音全拼搜索,如果能匹配,则需要匹配分离的汉字和拼音

	string str_pinyin = ChineseConvertPinYinAllSpell(strlower);
	string key_pinyin = ChineseConvertPinYinAllSpell(keylower);
	pos = str_pinyin.find(key_pinyin);
	if(pos != string::npos)
	{
		size_t str_index = 0;
		size_t pinyin_index = 0;

		size_t hightlight_index = 0;
		size_t hightlight_len = 0;

		while(str_index < strlower.size())
		{
			if(pinyin_index == pos)
			{
				hightlight_index = str_index;
			}
			if(pinyin_index >= pos + key_pinyin.size())
			{
				hightlight_len = str_index - hightlight_index + 1;
				break;
			}
			//判断原字符串str_index下标所指是否为字符
			if(strlower[str_index]>=0 && strlower[str_index]<=127)
			{
				//是字符
				str_index++;
				pinyin_index++;
			}
			else
			{
				//是汉字
				string  word(strlower, str_index, 2); //提取一个汉字
				string  subpinyin = ChineseConvertPinYinAllSpell(word);
				str_index += 2;
				pinyin_index += subpinyin.size();
			}
		}
		prefix = str.substr(0, hightlight_index);
		hightlight = str.substr(hightlight_index, hightlight_len);
		suffix = str.substr(hightlight_index+hightlight_len, string::npos);
		return;
	}

	//3 首字母搜索 如果能匹配,则需要匹配分离汉字和首字母
	std::string key_initials = ChineseConvertPinYinInitials(keylower);
	std::string str_initials = ChineseConvertPinYinInitials(strlower);
	pos = str_initials.find(key_initials);
	if (pos != std::string::npos)
	{
		size_t str_index = 0;
		size_t initials_index = 0;

		size_t highlight_index = 0;
		size_t highlight_len = 0;

		while (str_index < strlower.size())
		{
			// 如果拼音位置已经走到子串匹配的位置,则原串的位置就是高亮的起始位置
			if (initials_index == pos)
			{
				highlight_index = str_index;
			}

			// 如果拼音位置已经走完keylower的位置,则原串的位置就是高亮的结束位置
			if (initials_index >= pos + key_initials.size())
			{
				highlight_len = str_index - highlight_index + 1;
				break;
			}

			if (strlower[str_index] >= 0 && strlower[str_index] <= 127)
			{
				// 如果是字符则各跳一个位置
				++str_index;
				++initials_index;
			}
			else
			{
				// 如果是汉字原串的汉字跳两个字节(gbk的汉字是两个字符),首字母跳一个
				str_index += 2;
				++initials_index;
			}
		}

		prefix = str.substr(0, highlight_index);
		hightlight = str.substr(highlight_index, highlight_len);
		suffix = str.substr(highlight_index + highlight_len, std::string::npos);
		return;
	}

	//搜索失败,按照不高亮处理
	prefix = str;
	hightlight.clear();
	suffix.clear();
}

(四)系统驱动模块

#include"Common.h"
#include"Sysutil.h"

#include"ScanManager.h"
#include"DataManager.h"

char *title = "文档快速搜索工具";

int main()
{
	//创建扫描实例
	const string path = "E:\\Users\\Documents\\计算机专业课件";
	//ScanManager sm;
	ScanManager::CreateInstance(path).ScanDirectory(path);

	//创建搜索实例
	string key;
	vector<pair<string, string>> doc_path; //用于保存搜索的结果
	DataManager &dm = DataManager::CreateInstance();

	int row = 5;
	int count;
	while(1)
	{
		DrawFrame(title);
		DrawMenu();
		cin>>key;
		if(key == string("exit"))
			break;
		dm.Search(key, doc_path);

		//显示结果
		count = 0;
		for(auto &e : doc_path)
		{
			string &name = e.first; //
			string &path = e.second;

			//高亮处理
			string prefix, hightlight, suffix;
			DataManager::SplitHightLight(name, key, prefix, hightlight, suffix);

			SetCurPos(row+count++, 2);
			printf("%s",prefix.c_str());
			ColourPrintf(hightlight);
			printf("%-20s", suffix.c_str());

			SetCurPos(row+count-1, 2+30);
			printf("%-85s\n", path.c_str());
		}
		doc_path.clear();
		
		SystemEnd();
		system("pause");
	}

	SystemEnd();
	return 0;
}

展示界面界面展示

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值