做这个项目是由于linux下面有find指令可以查找文档非常快速,windows下面的搜索文档却非常的慢,是通过暴力搜索的,但是有个叫everything的神器可以解决这个问题,可以将文档同步到数据库,直接查找数据库进行搜索,速度快了很多,我也想做一个类似于everthing的东西。
我需要我的程序
1.支持文档普通搜索
2.支持拼音全拼搜索
3.支持拼音首字母搜索
4.支持搜索关键字高亮
这个是项目的框架,确定好框架就开始一步步实现。
代码块分为五个部分,
- 公共模块 : Common.h
- 系统工具模块 : Sysutil.h Sysutil.cpp
- 数据管理模块 : DataManager.h DataManager.cpp
- 扫描管理模块 : ScanManager.h ScanManager.cpp
- 系统驱动模块 : DocFastSearchTool.cpp
第一部分包含了我们所需要的头文件
#pragma once
#include<iostream>
#include<string>
#include<vector>
#include<set>
using namespace std;
再配置一下数据库
#pragma once
//数据库配置
#define DOC_DB "doc.db"
//数据库表配置
#define DOC_TB "doc_info"
//日志配置
//#define __TRACE__
#define __ERROR__
文件系统监控实现:
everything在实现这个模块时,使用了扫描+监控的实现方式,这两种方式是一种互补的方式。
1. 文件系统监控是利用系统文件系统的接口可以监控某个目录下的文档变化,优点是效率高,实时性强,缺点是监控是实时的,如果在监控程序没有启动期间的,文档的变化无法获取。
2. 文件系统扫描是通过系统接口,遍历获取目录下的所有文档跟数据库中的文档进行对比,获取文档变化,优点是监控程序启动前,变化的文档也能对比出来,缺点是性能低实时性不强。我们这里呢,为了简单一点,我们使用了简单粗暴的扫描。如有需要大家可以下去扩展实现可以加上监控。
数据持久化:
数据持久化我们使用了轻量级的一个数据库sqlite管理,使用sqlite需要去下载sqlite的源码或者源 码。我们的数据库的表很简单,只需要一张表
create table if not exists tb_doc (id INTEGER PRIMARY KEY autoincrement,
doc_path text, doc_name text, doc_name_pinyin text, doc_name_initials
text);
sysutil.cpp配置(高亮,模糊匹配)
#include"sysutil.h"
#include"log.h"
#include<io.h>
#include<windows.h>
//系统工具 -- 体现为函数
void DirectionList(const string &path, vector<string> &sub_dir, vector<string> &sub_file)
{
struct _finddata_t file;
//"C:\\Bit\\Code\\bit77\\Pro_文档快速搜索工具\\TestDoc";
string _path = path;
//"C:\\Bit\\Code\\bit77\\Pro_文档快速搜索工具\\TestDoc\\*.*";
_path += "\\*.*";
long handle = _findfirst(_path.c_str(), &file);
if(handle == -1)
{
//printf("扫描目录失败.\n");
ERROR_LOG("扫描目录失败.");
return;
}
do
{
if(file.name[0] == '.')
continue;
//cout<<file.name<<endl;
if(file.attrib & _A_SUBDIR)
sub_dir.push_back(file.name);
else
sub_file.push_back(file.name);
if(file.attrib & _A_SUBDIR)
{
//文件为目录(文件夹)
//"C:\\Bit\\Code\\bit77\\Pro_文档快速搜索工具\\TestDoc"
string tmp_path = path;
//"C:\\Bit\\Code\\bit77\\Pro_文档快速搜索工具\\TestDoc\\"
tmp_path += "\\";
//"C:\\Bit\\Code\\bit77\\Pro_文档快速搜索工具\\TestDoc\\dir_A"
tmp_path += file.name;
//目录递归遍历
DirectionList(tmp_path, sub_dir, sub_file);
}
}while(_findnext(handle,&file) == 0);
_findclose(handle);
}
/*
void DirectionList(const string &path)
{
struct _finddata_t file;
//"C:\\Bit\\Code\\bit77\\Pro_文档快速搜索工具\\TestDoc";
string _path = path;
//"C:\\Bit\\Code\\bit77\\Pro_文档快速搜索工具\\TestDoc\\*.*";
_path += "\\*.*";
long handle = _findfirst(_path.c_str(), &file);
if(handle == -1)
{
printf("扫描目录失败.\n");
return;
}
do
{
if(file.name[0] == '.')
continue;
cout<<file.name<<endl;
if(file.attrib & _A_SUBDIR)
{
//文件为目录(文件夹)
//"C:\\Bit\\Code\\bit77\\Pro_文档快速搜索工具\\TestDoc"
string tmp_path = path;
//"C:\\Bit\\Code\\bit77\\Pro_文档快速搜索工具\\TestDoc\\"
tmp_path += "\\";
//"C:\\Bit\\Code\\bit77\\Pro_文档快速搜索工具\\TestDoc\\dir_A"
tmp_path += file.name;
//目录递归遍历
DirectionList(tmp_path);
}
}while(_findnext(handle,&file) == 0);
_findclose(handle);
}
*/
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[YWJKGKLJYWKPJ"
"QHY[W[DZLSGMRYPYWWCCKZNKYYGTTNJJNYKKZYTCJNMCYLQLYPYQFQRPZSLWBTGKJFYX"
"JWZLTBNCXJJJJTXDTTSQZYCDXXHGCK[PHFFSS[YBGXLPPBYLL[HLXS[ZM[JHSOJNGHDZ"
"QYKLGJHSGQZHXQGKEZZWYSCSCJXYEYXADZPMDSSMZJZQJYZC[J[WQJBYZPXGZNZCPWHK"
"XHQKMWFBPBYDTJZZKQHY"
"LYGXFPTYJYYZPSZLFCHMQSHGMXXSXJ[[DCSBBQBEFSJYHXWGZKPYLQBGLDLCCTNMAYD"
"DKSSNGYCSGXLYZAYBNPTSDKDYLHGYMYLCXPY[JNDQJWXQXFYYFJLEJPZRXCCQWQQSBNK"
"YMGPLBMJRQCFLNYMYQMSQYRBCJTHZTQFRXQHXMJJCJLXQGJMSHZKBSWYEMYLTXFSYDSW"
"LYCJQXSJNQBSCTYHBFTDCYZDJWYGHQFRXWCKQKXEBPTLPXJZSRMEBWHJLBJSLYYSMDXL"
"CLQKXLHXJRZJMFQHXHWY"
"WSBHTRXXGLHQHFNM[YKLDYXZPYLGG[MTCFPAJJZYLJTYANJGBJPLQGDZYQYAXBKYSEC"
"JSZNSLYZHSXLZCGHPXZHZNYTDSBCJKDLZAYFMYDLEBBGQYZKXGLDNDNYSKJSHDLYXBCG"
"HXYPKDJMMZNGMMCLGWZSZXZJFZNMLZZTHCSYDBDLLSCDDNLKJYKJSYCJLKWHQASDKNHC"
"SGANHDAASHTCPLCPQYBSDMPJLPZJOQLCDHJJYSPRCHN[NNLHLYYQYHWZPTCZGWWMZFFJ"
"QQQQYXACLBHKDJXDGMMY"
"DJXZLLSYGXGKJRYWZWYCLZMSSJZLDBYD[FCXYHLXCHYZJQ[[QAGMNYXPFRKSSBJLYXY"
"SYGLNSCMHZWWMNZJJLXXHCHSY[[TTXRYCYXBYHCSMXJSZNPWGPXXTAYBGAJCXLY[DCCW"
"ZOCWKCCSBNHCPDYZNFCYYTYCKXKYBSQKKYTQQXFCWCHCYKELZQBSQYJQCCLMTHSYWHMK"
"TLKJLYCXWHEQQHTQH[PQ[QSCFYMNDMGBWHWLGSLLYSDLMLXPTHMJHWLJZYHZJXHTXJLH"
"XRSWLWZJCBXMHZQXSDZP"
"MGFCSGLSXYMJSHXPJXWMYQKSMYPLRTHBXFTPMHYXLCHLHLZYLXGSSSSTCLSLDCLRPBH"
"ZHXYYFHB[GDMYCNQQWLQHJJ[YWJZYEJJDHPBLQXTQKWHLCHQXAGTLXLJXMSL[HTZKZJE"
"CXJCJNMFBY[SFYWYBJZGNYSDZSQYRSLJPCLPWXSDWEJBJCBCNAYTWGMPAPCLYQPCLZXS"
"BNMSGGFNZJJBZSFZYNDXHPLQKZCZWALSBCCJX[YZGWKYPSGXFZFCDKHJGXDLQFSGDSLQ"
"WZKXTMHSBGZMJZRGLYJB"
"PMLMSXLZJQQHZYJCZYDJWBMYKLDDPMJEGXYHYLXHLQYQHKYCWCJMYYXNATJHYCCXZPC"
"QLBZWWYTWBQCMLPMYRJCCCXFPZNZZLJPLXXYZTZLGDLDCKLYRZZGQTGJHHGJLJAXFGFJ"
"ZSLCFDQZLCLGJDJCSNZLLJPJQDCCLCJXMYZFTSXGCGSBRZXJQQCTZHGYQTJQQLZXJYLY"
"LBCYAMCSTYLPDJBYREGKLZYZHLYSZQLZNWCZCLLWJQJJJKDGJZOLBBZPPGLGHTGZXYGH"
"ZMYCNQSYCYHBHGXKAMTX"
"YXNBSKYZZGJZLQJDFCJXDYGJQJJPMGWGJJJPKQSBGBMMCJSSCLPQPDXCDYYKY[CJDDY"
"YGYWRHJRTGZNYQLDKLJSZZGZQZJGDYKSHPZMTLCPWNJAFYZDJCNMWESCYGLBTZCGMSSL"
"LYXQSXSBSJSBBSGGHFJLYPMZJNLYYWDQSHZXTYYWHMZYHYWDBXBTLMSYYYFSXJC[DXXL"
"HJHF[SXZQHFZMZCZTQCXZXRTTDJHNNYZQQMNQDMMG[YDXMJGDHCDYZBFFALLZTDLTFXM"
"XQZDNGWQDBDCZJDXBZGS"
"QQDDJCMBKZFFXMKDMDSYYSZCMLJDSYNSBRSKMKMPCKLGDBQTFZSWTFGGLYPLLJZHGJ["
"GYPZLTCSMCNBTJBQFKTHBYZGKPBBYMTDSSXTBNPDKLEYCJNYDDYKZDDHQHSDZSCTARLL"
"TKZLGECLLKJLQJAQNBDKKGHPJTZQKSECSHALQFMMGJNLYJBBTMLYZXDCJPLDLPCQDHZY"
"CBZSCZBZMSLJFLKRZJSNFRGJHXPDHYJYBZGDLQCSEZGXLBLGYXTWMABCHECMWYJYZLLJ"
"JYHLG[DJLSLYGKDZPZXJ"
"YYZLWCXSZFGWYYDLYHCLJSCMBJHBLYZLYCBLYDPDQYSXQZBYTDKYXJY[CNRJMPDJGKL"
"CLJBCTBJDDBBLBLCZQRPPXJCJLZCSHLTOLJNMDDDLNGKAQHQHJGYKHEZNMSHRP[QQJCH"
"GMFPRXHJGDYCHGHLYRZQLCYQJNZSQTKQJYMSZSWLCFQQQXYFGGYPTQWLMCRNFKKFSYYL"
"QBMQAMMMYXCTPSHCPTXXZZSMPHPSHMCLMLDQFYQXSZYYDYJZZHQPDSZGLSTJBCKBXYQZ"
"JSGPSXQZQZRQTBDKYXZK"
"HHGFLBCSMDLDGDZDBLZYYCXNNCSYBZBFGLZZXSWMSCCMQNJQSBDQSJTXXMBLTXZCLZS"
"HZCXRQJGJYLXZFJPHYMZQQYDFQJJLZZNZJCDGZYGCTXMZYSCTLKPHTXHTLBJXJLXSCDQ"
"XCBBTJFQZFSLTJBTKQBXXJJLJCHCZDBZJDCZJDCPRNPQCJPFCZLCLZXZDMXMPHJSGZGS"
"ZZQLYLWTJPFSYASMCJBTZKYCWMYTCSJJLJCQLWZMALBXYFBPNLSFHTGJWEJJXXGLLJST"
"GSHJQLZFKCGNNNSZFDEQ"
"FHBSAQTGYLBXMMYGSZLDYDQMJJRGBJTKGDHGKBLQKBDMBYLXWCXYTTYBKMRTJZXQJBH"
"LMHMJJZMQASLDCYXYQDLQCAFYWYXQHZ";
const char* cName = name.c_str();
std::string result;
int H = 0, L = 0, W = 0, j = 0;
size_t stringlen = ::strlen(cName);
for (int 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 char* str)
{
// 0-黑 1-蓝 2-绿 3-浅绿 4-红 5-紫 6-黄 7-白 8-灰 9-淡蓝 10-淡绿
// 11-淡浅绿 12-淡红 13-淡紫 14-淡黄 15-亮白
//颜色:前景色 + 背景色*0x10
//例如:字是红色,背景色是白色,即 红色 + 亮白 = 4 + 15*0x10
WORD color = 9 + 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);
SetConsoleTextAttribute(handle, colorOld);
}
数据管理模块:
#pragma once
#include"common.h"
#include"./sqlite3/sqlite3.h"
//封装数据库sqlite
class SqliteManager
{
public:
SqliteManager();
~SqliteManager();
public:
void Open(const string &database); //打开或创建数据库
void Close(); //关闭数据库
void ExecuteSql(const string &sql); //执行SQL
void GetResultTable(const string &sql, char **&ppRet, int &row, int &col);
private:
sqlite3 *m_db;
};
class AutoGetResultTable
{
public:
AutoGetResultTable(SqliteManager &db, const string &sql, char **&ppRet, int &row, int &col);
~AutoGetResultTable();
private:
SqliteManager &m_db;
char **m_ppRet;
};
#define SQL_BUFFER_SIZE 256
//封装数据管理类
class DataManager
{
public:
static DataManager& GetInstance();
public:
~DataManager();
public:
void Search(const string &key, vector<pair<string,string>> &doc_path);
public:
void InitSqlite(); //初始化数据库
void InsertDoc(const string &path, const string &doc);
void DeleteDoc(const string &path, const string &doc);
void GetDoc(const string &path, multiset<string> &docs);
public:
static void SplitHighLight(const string &str, const string &key,
string &prefix, string &highlight, string &suffix);
protected:
DataManager();
DataManager(const DataManager &);
DataManager& operator=(const DataManager &);
private:
SqliteManager m_dbmgr;
};
#include"dataManager.h"
#include"log.h"
#include"config.h"
#include"sysutil.h"
#include<algorithm>
#pragma comment(lib, "./sqlite3/sqlite3.lib")
SqliteManager::SqliteManager():m_db(nullptr)
{}
SqliteManager::~SqliteManager()
{
Close(); //关闭数据库
}
void SqliteManager::Open(const string &database)
{
int rc = sqlite3_open(database.c_str(), &m_db);
if (rc != SQLITE_OK)
{
//fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(m_db));
ERROR_LOG("Can't open database: %s\n", sqlite3_errmsg(m_db));
exit(1);
}
else
{
//fprintf(stderr, "Opened database successfully\n");
TRACE_LOG("Opened database successfully");
}
}
void SqliteManager::Close()
{
int rc = sqlite3_close(m_db);
if (rc != SQLITE_OK)
{
//fprintf(stderr, "Can't close database: %s\n", sqlite3_errmsg(m_db));
ERROR_LOG("Can't close database: %s\n", sqlite3_errmsg(m_db));
exit(1);
}
else
{
//fprintf(stderr, "Close database successfully\n");
TRACE_LOG( "Close database successfully");
}
}
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)
{
//fprintf(stderr, "SQL error: %s\n", zErrMsg);
ERROR_LOG("SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else
{
//fprintf(stdout, "Operation sql successfully\n");
TRACE_LOG("Operation sql successfully");
}
}
void SqliteManager::GetResultTable(const string &sql, char **&ppRet, int &row, int &col)
{
char *zErrMsg = 0;
int rc = sqlite3_get_table(m_db, sql.c_str(), &ppRet, &row, &col, &zErrMsg);
if(rc != SQLITE_OK)
{
//fprintf(stderr, "SQL Error: %s\n", zErrMsg);
ERROR_LOG("SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else
{
//fprintf(stdout, "Get Result Table successfully\n");
TRACE_LOG("Get Result Table successfully");
}
}
///
AutoGetResultTable::AutoGetResultTable(SqliteManager &db, const string &sql,
char **&ppRet, int &row, int &col)
:m_db(db), m_ppRet(nullptr)
{
m_db.GetResultTable(sql, ppRet, row, col);
m_ppRet = ppRet;
}
AutoGetResultTable::~AutoGetResultTable()
{
if(m_ppRet)
sqlite3_free_table(m_ppRet);
}
///
DataManager& DataManager::GetInstance()
{
//懒汉模式
static DataManager _inst;
return _inst;
}
DataManager::DataManager()
{
m_dbmgr.Open(DOC_DB);
InitSqlite(); //创建表
}
DataManager::~DataManager()
{}
void DataManager::InitSqlite()
{
char sql[SQL_BUFFER_SIZE] = {0};
sprintf(sql, "CREATE TABLE if not exists %s(\
id integer primary key autoincrement,\
doc_name text,\
doc_name_py text,\
doc_name_initials text,\
doc_path text)", DOC_TB);
m_dbmgr.ExecuteSql(sql);
}
void DataManager::InsertDoc(const string &path, const string &doc)
{
//汉字转拼音
string doc_py = ChineseConvertPinYinAllSpell(doc);
//汉字转首字母
string doc_initials = ChineseConvertPinYinInitials(doc);
char sql[SQL_BUFFER_SIZE] = {0};
sprintf(sql, "INSERT INTO %s values(null, '%s', '%s','%s', '%s')",
DOC_TB, doc.c_str(), doc_py.c_str(), doc_initials.c_str(), path.c_str());
m_dbmgr.ExecuteSql(sql);
}
void DataManager::DeleteDoc(const string &path, const string &doc)
{
char sql[SQL_BUFFER_SIZE] = {0};
sprintf(sql, "DELETE FROM %s where doc_path='%s' and doc_name='%s'",
DOC_TB, path.c_str(), doc.c_str());
m_dbmgr.ExecuteSql(sql);
}
void DataManager::GetDoc(const string &path, multiset<string> &docs)
{
char sql[SQL_BUFFER_SIZE] = {0};
sprintf(sql, "SELECT doc_name from %s where doc_path='%s'",
DOC_TB, path.c_str());
char **ppRet = 0;
int row = 0, col = 0;
//m_dbmgr.GetResultTable(sql, ppRet, row, col);
AutoGetResultTable at(m_dbmgr, sql, ppRet, row, col);
for(int i=1; i<=row; ++i)
docs.insert(ppRet[i]);
//释放表结果
//sqlite3_free_table(ppRet);
}
void DataManager::Search(const string &key, vector<pair<string,string>> &doc_path)
{
//汉字转拼音
string doc_py = ChineseConvertPinYinAllSpell(key);
//汉字转首字母
string doc_initials = ChineseConvertPinYinInitials(key);
char sql[SQL_BUFFER_SIZE] = {0};
sprintf(sql, "SELECT doc_name, doc_path from %s where doc_name like '%%%s%%' or\
doc_name_py like '%%%s%%' or doc_name_initials like '%%%s%%'",
DOC_TB, key.c_str(), doc_py.c_str(), doc_initials.c_str());
char **ppRet;
int row, col;
//m_dbmgr.GetResultTable(sql, ppRet, row, col);
AutoGetResultTable at(m_dbmgr, sql, ppRet, row, col);
doc_path.clear(); //清除之前搜索的数据
for(int i=1; i<=row; ++i)
{
doc_path.push_back(make_pair(ppRet[i*col], ppRet[i*col+1]));
}
//释放表结果
//sqlite3_free_table(ppRet);
}
void DataManager::SplitHighLight(const string &str, const string &key,
string &prefix, string &highlight, string &suffix)
{
//忽略大小的匹配
string strlower = str;
string keylower = key;
transform(strlower.begin(), strlower.end(), strlower.begin(), tolower);
transform(keylower.begin(), keylower.end(), keylower.begin(), tolower);
//原始字符串能够匹配
size_t pos = strlower.find(keylower);
if(pos != string::npos)
{
prefix = str.substr(0, pos);
highlight = str.substr(pos, keylower.size());
suffix = str.substr(pos+keylower.size(), str.size());
return;
}
//拼音全拼搜索分割
string str_py = ChineseConvertPinYinAllSpell(strlower);
pos = str_py.find(keylower);
if(pos != string::npos)
{
int str_index = 0; //控制原始字符串的下标
int py_index = 0; //控制拼音字符串的下标
int highlight_index = 0; //控制高亮显示字符串的起始位置
int highlight_len = 0; //控制高亮字符串的长度
while(str_index < str.size())
{
if(py_index == pos)
{
//记录高亮的起始位置
highlight_index = str_index;
}
if(py_index >= pos+keylower.size())
{
//关键字搜索结束
highlight_len = str_index - highlight_index;
break;
}
if(str[str_index]>=0 && str[str_index]<=127)
{
//原始字符串是一个字符
str_index++;
py_index++;
}
else
{
//原始字符串是一个汉字
string word(str, str_index, 2); //截取一个汉字 //校
string word_py = ChineseConvertPinYinAllSpell(word);//xiao
str_index += 2;
py_index += word_py.size();
}
}
prefix = str.substr(0, highlight_index);
highlight = str.substr(highlight_index, highlight_len);
suffix = str.substr(highlight_index+highlight_len, str.size());
return;
}
//首字母搜索
string str_initials = ChineseConvertPinYinInitials(strlower);
pos = str_initials.find(keylower);
if(pos != string::npos)
{
int str_index = 0;
int initials_index = 0;
int highlight_index = 0;
int highlight_len = 0;
while(str_index < str.size())
{
if(initials_index == pos)
{
//记录高亮的起始位置
highlight_index = str_index;
}
if(initials_index >= pos+keylower.size())
{
highlight_len = str_index - highlight_index;
break;
}
if(str[str_index]>=0 && str[str_index]<=127)
{
//原始字符串是一个字符
str_index++;
initials_index++;
}
else
{
//原始字符串是一个汉字
str_index += 2;
initials_index++;
}
}
prefix = str.substr(0, highlight_index);
highlight = str.substr(highlight_index, highlight_len);
suffix = str.substr(highlight_index+highlight_len, str.size());
return;
}
//没有搜索到关键字
prefix = str;
highlight.clear();
suffix.clear();
}
扫描管理模块:
#pragma once
#include"common.h"
#include"dataManager.h"
#include<windows.h>
#include<mutex>
#include<condition_variable>
class ScanManager
{
public:
static ScanManager& GetInstance(const string &path);
public:
//同步数据
void ScanDirectory(const string &path);
//扫描线程
void ScanThread(const string &path);
//监控线程
void WatchThread(const string &path);
protected:
ScanManager(const string &path);
ScanManager(ScanManager &);
ScanManager& operator=(const ScanManager&);
private:
//DataManager m_dbmgr;
mutex m_mutex;
condition_variable m_cond;
};
#include"ScanManager.h"
#include"sysutil.h"
#include"log.h"
#include<thread>
ScanManager& ScanManager::GetInstance(const string &path)
{
static ScanManager _inst(path);
return _inst;
}
void ScanManager::ScanThread(const string &path)
{
//初始化扫描
ScanDirectory(path);
while(1)
{
unique_lock<mutex> lock(m_mutex);
m_cond.wait(lock); //条件阻塞
ScanDirectory(path);
}
}
void ScanManager::WatchThread(const string &path)
{
HANDLE hd = FindFirstChangeNotification(path.c_str(), true,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME);
if(hd == INVALID_HANDLE_VALUE)
{
//cout<<"监控目录失败."<<endl;
ERROR_LOG("监控目录失败.");
return;
}
while(1)
{
WaitForSingleObject(hd, INFINITE); //永不超时等待
m_cond.notify_one();
FindNextChangeNotification(hd);
}
}
ScanManager::ScanManager(const string &path)
{
//扫描对象
thread ScanObj(&ScanManager::ScanThread, this, path);
ScanObj.detach();
//监控对象
thread WatchObj(&ScanManager::WatchThread, this, path);
WatchObj.detach();
}
//同步本地文件和数据库文件的数据
void ScanManager::ScanDirectory(const string &path)
{
//1 扫描本地文件
vector<string> local_dir;
vector<string> local_file;
DirectionList(path, local_dir, local_file);
multiset<string> local_set;
local_set.insert(local_file.begin(), local_file.end());
local_set.insert(local_dir.begin(), local_dir.end());
//2 扫描数据库文件
multiset<string> db_set;
DataManager &m_dbmgr = DataManager::GetInstance(); //注意一定使用引用接收
m_dbmgr.GetDoc(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_dbmgr.InsertDoc(path, *local_it);
++local_it;
}
else if(*local_it > *db_it)
{
//本地没有,数据库有,数据库删除文件
m_dbmgr.DeleteDoc(path, *db_it);
++db_it;
}
else
{
//两者都有
++local_it;
++db_it;
}
}
while(local_it != local_set.end())
{
//本地有,数据库没有,数据库插入文件
m_dbmgr.InsertDoc(path, *local_it);
++local_it;
}
while(db_it != db_set.end())
{
//本地没有,数据库有,数据库删除文件
m_dbmgr.DeleteDoc(path, *db_it);
++db_it;
}
}
驱动模块:
#include"sysutil.h"
#include"dataManager.h"
#include"ScanManager.h"
#include"log.h"
#include"sysFrame.h"
const char *title = "文档快速搜索工具";
int main(int argc, char *argv[])
{
const string path = "C:\\Bit\\Code\\bit77\\Pro_文档快速搜索工具\\TestDoc";
//const string path = "C:\\Bit\\Book";
//扫描目录
ScanManager &sm = ScanManager::GetInstance(path);
//搜索
DataManager &dm = DataManager::GetInstance();
vector<pair<string,string>> doc_path;
string key;
while(1)
{
//显示界面
DrawFrame(title);
DrawMenu();
cin>>key;
if(key == "exit")
break;
dm.Search(key, doc_path);
int row = 5; //默认5行
int count = 0; //显示的行数
string prefix, highlight, suffix;
for(const auto &e : doc_path) //e : doc_name doc_path
{
//高亮分割
string doc_name = e.first;
DataManager::SplitHighLight(doc_name, key, prefix, highlight, suffix);
//设置文档名显示位置
SetCurPos(2, row+count++);
cout<<prefix;
ColourPrintf(highlight.c_str());
cout<<suffix;
//设置路劲名显示位置
SetCurPos(33, row+count-1);
printf("%--85s\n", e.second.c_str());
}
SystemEnd();
SystemPause();
}
SystemEnd();
return 0;
}