windows下的文档搜索工具

1、项目背景

 在windows下进行磁盘查找速度过慢(可以为文件建立索引来提高查找效率)。因此参考everything,借助于数据库,设计一个自己的文档搜索工具。

2、everything原理

 Everything并不扫描整个磁盘,只是读取磁盘上的USN日志,并建立索引,所以速度飞快。
但因此缺点也明显:
 1、只支持NTFS格式的分区,因为USN日志是NTFS专有的。在FAT、FAT32格式分区上无法使用
Everything。
 2、只索引文件名称、日期和大小,不索引文件内容和附加属性,而windows建立的索引是会检索文件内容的。
 ,Everything由于核心原理建立在NTFS的底层机制上,NTFS文件系统中的 USN 日志记录了系统对NTFS分区中的文件所做的所有更改。对NTFS分区,文件信息存储于主文件表(Master File Table,MFT)。MFT是NTFS的核心。主文件表里的每条记录包含文件和目录的所有信息,例如文件名/目录名、路径、大小、属性等。此外,还会存储该文件/目录最后一次变化对应的USN,系统在更新USN Journal时,也会更新这个字段。

3、项目分析

1.项目需求
1.支持文档普通搜索
2.支持拼音全拼搜索
3.支持拼音首字母搜索
4.支持搜索关键字高亮
2.开发环境
1.编译器 : VS2013 / 控制应用平台
2.编程语言 : C++ / C++11
2.数据库 : sqlite3
3.项目涉及的知识点
1.数据库操作:(sqlite安装,创建数据库,创建表,插入数据,删除数据,创建索引,查询数据
(条件查询、 模糊查询))
2.静态库和动态库:静态库和动态的制作,动态库和动态的使用
3.设计模式(单例模式)
4.多线程
5.同步机制(互斥量)
6.日志
7.汉字与拼音的转换

4、设计框架

在这里插入图片描述
包括这几个模块:
公共模块 : Common.h
 定义头文件信息和一些宏
系统工具模块 : Sysutil.h Sysutil.cpp
 包含一些基本的工具函数,汉字转拼音,汉字转首字母,搜索本地文件
数据管理模块 : DataManager.h DataManager.cpp
 封装sqlite3的函数,随后实现增删改查语句的执行和关键字高亮
扫描管理模块 : ScanManager.h ScanManager.cpp
 主要模块,扫描本地文件和获得数据库文件,对数据库进行增删改查
系统驱动模块 : DocFastSearchTool.cpp (测试)ScanManager.cpp
效果如下:
在这里插入图片描述

5、项目设计

全部代码见GitHub

公共模块
#pragma once

#include<iostream>
#include<string>
#include<vector>
#include<set>
#include<map>
#include"sqlite\sqlite3.h"
#include<io.h>
#include<thread>
#include<chrono>
#include<windows.h>
#include<algorithm>
using namespace std;

#define SQL_BUFFER_SIZE 256
#define MAX_TITLE_SIZE 128
数据管理模块 DataManager.h

分为两部分:第一部分是对数据库SqliteManager的封装;同时定义一个自动获取表机制AutoGetResultTable,通过析构函数实现在该类实例对象生命周期结束时释放结果表。随后DataManager实现数据库的增删改查。

注意

1、项目中只是针对GB2312中的汉字做转换。在GB2312中,一个字母或者数字占一个字节,一个汉字则占了两个字节。
2、InitSqlite()函数通过执行sql语句创建一个表单,表单中包含如下项目:序号、文件名、文件路径、文件拼音全拼、文件拼音首字母全拼。这个类在构造对象时构造函数会打开或者创建一个数据库,然后调用InitSqlite();在这个数据库中创建一张表。
3、DeleteDoc()函数用来删除数据库中的某个文件,如果删除的是文件,则直接删除。如果是目录文件,则删除相关目录下的所有文件。
4、Serach()函数搜索的时候是将用户输入转换为拼音和首字母,随后进行模糊匹配。
5、 SplitHighlight()函数在进行高亮的时候,核心是把需要高亮的内容分割出来,首先要将key都转换为小写,方便查找。随后把查找的内容分三种情况讨论,汉字,拼音,首字母。注意的是定位的时候汉字是+=2,两个字节。

#pragma once
#include"Common.h"
class SqliteManager
{
public:
	SqliteManager();
	~SqliteManager();
public:
	void Open(const string &path);
	void Close();
	void ExecuteSql(const string &sql);
	void GetResultTable(const string &sql, int &row, int &col, char **&ppRet);
private:
	sqlite3 *m_db;
};

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

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

///单例模式
class DataManager
{
public:
	static DataManager& GetInstance();
public:
	~DataManager();
public:
	void InitSqlite();
public:
	void InsertDoc(const string &path, const string &doc);
	void GetDocs(const string &path, multiset<string> &docs);
	void DeleteDoc(const string &path, const string &doc);
public:
	void Search(const string &key, vector<pair<string,string>> &doc_path);
	static void SplitHighlight(const string &str, const string &key, 
							   string &prefix, string &highlight, string &suffix);
private:
	DataManager();
private:
	SqliteManager m_dbmgr;
};
扫描模块SanManager.h

创建监控线程专门进行扫描,主线程进行对比数据库的内容和本地文件内容是否一致。

#pragma once
#include"Common.h"
#include"DataManager.h"

class ScanManager
{
public:
	static ScanManager& CreateInstance(const string &path);
public:
	void StartScan(const string &path);
	void StartWatch(); // 监控线程
	void ScanDirectory(const string &path);
private:
	ScanManager();
private:
	//DataManager m_db;
};
5. 打印界面模块Sysframe.h
void SetCurPos(int x, int y);
void DrawRow(int x, int y);
void DrawCol(int x, int y);
void DrawFrame(char *title);
void DrawMenu();
void SystemEnd();
void HideCursor();
工具模块Sysutil.h

//中间逻辑层
void DirectionList(const string &path, vector<string> &subfile, vector<string> &subdir);
std::string ChineseConvertPinYinAllSpell(const std::string& dest_chinese);
std::string ChineseConvertPinYinInitials(const std::string& name);
void ColourPrintf(const char* str);

6、 遇到的问题

1、汉字转拼音如何转换,刚开始想的是想把输入的内容转换成拼音存储到数据库中。但是毫无头绪,在网上查资料发现,可以根据编码范围确定汉字拼音。此方法的弊端是一些生僻字无法处理,比如:“囧”。也无法转换出声调。但也解决了燃眉之急。
原理:
根据编码范围确定汉字拼音。这里我用的是gb2312编码。由于区位码与汉字一一对应。所以我们可以根据区位码找到对应的汉字。因gb2312的第16-55区是按拼音顺序排列的一级汉字(比较常用的汉字)。所以用范围可以确定一级汉字的拼音,如区位码在[1601, 1603)集合,拼音为a;区位码在[1603,1616)集合,拼音为ai。而gb2312的第56-87区是按偏旁部首排列的二级汉字(不常用的汉子),我们就无法用范围确定一个字的拼音。只能列举出区位码对应的拼音。所以实现的话只需要将拼音和区位码对应起来即可。
在了解原理之后,下载源码测试成功,随后汉字转首字母就直接采用相同的代码,返回一个汉字拼音的时候只取首字母。
2、对输出的结果中关键字进行高亮处理,如若输入的是汉字,则直接使用find的函数截取到关键字在输出结果范围,随后进行高亮。但如果是拼音全拼搜索,由于拼音的长度和汉字的长度不同,所以如何根据拼音定位到字符串中的汉字纠结了好久。解决办法是先将查找出的内容转换成拼音,随后定位key的位置,最后根据拼音和汉字的规律定位key在汉字中的位置。首字母搜索同理。

7、 项目缺陷

1、汉字转拼音只能转常用的汉字。
2、和everything对比,扫描的是磁盘,而且只能在一个盘上进行扫描。
github https://github.com/dzq123321/project/tree/master/C%2B%2B%20%E6%96%87%E6%A1%A3%E6%90%9C%E7%B4%A2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值