【项目】---快速搜索工具

news2024/11/28 20:52:54

目录

一、项目背景

二、项目需求分析

三、项目涉及的知识点

四、项目实现的基础理论

五、项目框架

六、增加系统工具模块

6.1、扫描本地的文件的功能

七、增加数据管理模块

7.1、先了解数据库sqlite

7.2  封装sqlite数据库管理类

7.3、封装数据管理类

7.3.1增加搜索功能

7.3.2  利用RAII机制解决表结果的自动释放

八、新增扫描模块

8.1、同步函数,同步数据库和本地

8.2、新增实时扫描功能

8.3 扫描管理类的单例化

九、对sqlite进行静态链接库的使用

1、生成静态链接库

 2、使用生成静态链接库

十、新增监控模块

2、在扫描管理类中添加互斥量和条件变量

3、升级扫描线程和监控线程

十一、中间逻辑层实现

1、实现拼音全拼和首字母的搜索

2、实现高亮搜索

2.1 实现高亮搜索的关键是实现分割函数

十二、客户端的实现

项目中遇到的问题:

对比Everything


一、项目背景

inux环境下有非常好用的fifind命令,查找文档非常的便捷高效,而windows下文件夹框下的默认
搜索是搜索时在进行暴力遍历查找,查询的非常慢。

这时候就想着找一个能够快速搜索的工具,先是找到了Everything,用的时候发现这个工具搜索起来非常快,
但是在后面的时候发现这个工具又有一些        
                               缺点:  1、 不能使用拼音搜索、也不能使用首字母搜索
                                            2、只适用于 NTFS 格式 
但是QQ就支持这些功能:

 所以我就想着能不能自己来写一个快速搜索工具呢?

二、项目需求分析

  • 1、支持文档的常规搜索
  • 2、支持拼音全拼搜索
  • 3、支持拼音首字母搜索
  • 4、支持搜索关键字高亮显示
  • 5、扫描和监控(用户感知不到)

三、项目涉及的知识点

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

四、项目实现的基础理论

五、项目框架

六、增加系统工具模块

sysutil.h  和  sysutil.cpp

6.1、扫描本地的文件的功能

这个函数最终的功能是希望能把遍历的目录给保存下来(保存到数据库中)

能够扫描本地的文件首先要知道一些系统的函数:

//功能是搜索与指定的文件名称匹配的第一个实例,若成功则返回第一个实例的句柄,否则返回-1L
long _findfirst( char *filespec, struct _finddata_t *fileinfo );
//_findnext函数提供搜索文件名称匹配的下一个实例,若成功则返回0,否则返回-1
int _findnext( long handle, struct _finddata_t *fileinfo );
//_findclose用于释放由_findfirst分配的内存,可以停止一个_findfirst/_findnext序列
int _findclose( long handle );
在这些函数的帮助下我们就能够初步的扫描出本地的目录了
//系统工具 -- 体现为函数
void DirectionList(const string& path, vector<string>& sub_dir, vector<string>& sub_file)
{
	struct _finddata_t file;

	//"C:\\Users\\86188\\Desktop\\项目1\\项目—文档快速搜索工具\\TestDoc"
	string _path = path;

	//"C:\\Users\\86188\\Desktop\\项目1\\项目—文档快速搜索工具\\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:\\Users\\86188\\Desktop\\项目1\\项目—文档快速搜索工具\\TestDoc"
			string tmp_path = path;
			//"C:\\Users\\86188\\Desktop\\项目1\\项目—文档快速搜索工具\\TestDoc"
			tmp_path += "\\";
			//"C:\\Users\\86188\\Desktop\\项目1\\项目—文档快速搜索工具\\TestDoc"
			tmp_path += file.name;

			//目录递归遍历
			DirectionList(tmp_path, sub_dir, sub_file);
		}

	} while (_findnext(handle, &file) == 0);

	_findclose(handle);
}

七、增加数据管理模块

新增数据管理模块:dataManager.h     dataManager.cpp

7.1、先了解数据库sqlite

SQlite简介

SQLite 是一个软件库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。
SQLite 是一个增长最快的数据库引擎,这是在普及方面的增长,与它的尺寸大小无关。 SQLite 源代码不
受版权限制。
为什么要用 SQLite?
  • 不需要一个单独的服务器进程或操作的系统(无服务器的)。
  • SQLite 不需要配置,这意味着不需要安装或管理。(使用简单)
  • 一个完整的 SQLite 数据库是存储在一个单一的跨平台的磁盘文件。
  • SQLite 是非常小的,是轻量级的,完全配置时小于 400KiB,省略可选功能配- 置时小于250KiB。
  • SQLite 是自给自足的,这意味着不需要任何外部的依赖。
  • SQLite 事务是完全兼容 ACID 的,允许从多个进程或线程安全访问。
  • SQLite 支持 SQL92(SQL2)标准的大多数查询语言的功能。
  • SQLite 使用 ANSI-C 编写的,并提供了简单和易于使用的 API。

数据库操作的重要接口:

​
​//打开数据库
int sqlite3_open(const char *filename, sqlite3 **ppDb);
//关闭
int sqlite3_close(sqlite3*);
//执行操作  后面的创建表,插入数据其实就是把sql的内容换了而已
int sqlite3_exec(sqlite3*, const char *sql, sqlite_callback,
void *data, char **errmsg);

int sqlite3_get_table(
sqlite3 *db, /* An open database */
const char *zSql, /* SQL to be evaluated */
char ***pazResult, /* Results of the query */
int *pnRow, /* Number of result rows written here */
int *pnColumn, /* Number of result columns written here */
char **pzErrmsg /* Error msg written here */
);
void sqlite3_free_table(char **result);

​

​

有了这些函数,我们就可以把这些函数封装成一个类  SqliteManager

7.2  封装sqlite数据库管理类

//封装数据库sqlite
class SqliteManager
{
public:
	SqliteManager();
	~SqliteManager();

public:
	void Open(const string& database);  //打开或者创建一个数据库 
	void Close();					//关闭数据库
	void ExecuteSq1(const string& sql);//执行SQL  创建表,插入,删除都是通过执行sql语句
	void GetResultTable(const string& sql, char**& ppRet, int& row, int& col);

private:
	sqlite3* m_db;
};
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\n");
	}
}
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\n");
	}
}

void SqliteManager::ExecuteSq1(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\n");
	}

}

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\n");
	}


}

7.3、封装数据管理类

方便我们对数据库的操作,因为我们最后并不是要去对数据库进行操作,而是让本地文件和数据库的文件进行持续的对比,

确保本地文件和数据库的文件是同步的,也可以简单理解为我们并不直接去操作数据库

//封装数据管理类
class DataManager
{
public:
DataManager();
~DataManager();
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);
private:
SqliteManager m_dbmgr;
};
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_path text)", DOC_TB);
m_dbmgr.ExecuteSql(sql);
}
void DataManager::InsertDoc(const string &path, const string &doc)
{
char sql[SQL_BUFFER_SIZE] = {0};
sprintf(sql, "INSERT INTO %s values(null, '%s', '%s')",
DOC_TB, doc.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);
for(int i=1; i<=row; ++i)
docs.insert(ppRet[i]);
//释放表结果
sqlite3_free_table(ppRet);
}

7.3.1增加搜索功能

这里的搜索是用到了like模糊匹配

void DataManager::Search(const string &key, vector<pair<string,string>>
&doc_path)
{
char sql[SQL_BUFFER_SIZE] = {0};
sprintf(sql, "SELECT doc_name, doc_path from %s where doc_name like
'%%%s%%'",
DOC_TB, key.c_str());
char **ppRet;
int row, col;
m_dbmgr.GetResultTable(sql, ppRet, row, col);
for(int i=1; i<=row; ++i)
{
doc_path.push_back(make_pair(ppRet[i*col], ppRet[i*col+1]));
}
sqlite3_free_table(ppRet);
}

7.3.2  利用RAII机制解决表结果的自动释放

增加一个AutoGetResultTable类

我们会发现,在管理数据的时候,只要获取了表,就需要在最后面进行一步释放表结果的操作

我们万一忘记释放表结果就会导致内存泄漏,也就是说搜一次就会泄漏一次,如果搜的次数多的话,势必会导致内存资源被耗光

手动释放表结果还是有点麻烦的,而且我们也不能保证每次都记得去释放

所以我们就想着能不能让他自动的去释放呢?

这时候就想到了智能指针的思想 

class AutoGetResultTable
{
public:
	AutoGetResultTable(SqliteManager& db, const string& sql, char**& ppRet, int& row, int& col);
	~AutoGetResultTable();
private:
	SqliteManager& m_db;
	char** m_ppRet;
};
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);
}

小小问题:我们在写这个类的时候怎么知道要传哪些参数呢?我们怎么知道要有哪些成员呢?

本身这个类就是要解决的释放空间,那么我们要是不把空间保存下来,拿什么去释放呢?所以在类中就把ppRet给保留了。

有了智能指针以后,获取表实现起来就简单了点

八、新增扫描模块

ScanManager.h  和  ScanManager.cpp

扫描模块其实的作用就是让数据库同步本地的数据
实现同步的前提肯定是能让本地的数据和数据库的数据进行对比,有不同的地方就让数据库去进行更改
怎么能实现这个效果呢?
我们可以用到一个容器  multiset  , 借助这个容器的排序性(因为底层是红黑树)

8.1、同步函数,同步数据库和本地

//同步本地数据和数据库数据
void ScanManger::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;
	}
}

8.2、新增实时扫描功能

之前写的扫描是在搜索之前先进行了扫描,当程序跑起来之后,就无法再去同步数据库了,比如我们运行程序以后,这时候我们删除了一个文件,数据库是同步不了数据的,这就是个问题。我们要想同步就必须要把程序重启一下,这显然是不妥当的。

那有什么办法能实时的进行同步呢?

我们想要实时的进行一直扫描,就需要多线程的思想,让一个线程去专门扫描

在ScanManger的构造函数当中新增创建扫描线程

ScanManger::ScanManger(const string &path)
{
	//扫描对象 
	thread ScanObj(&ScanManger::ScanThread,this,path);
	ScanObj.detach();

}

线程的函数就是一直在做着扫描的工作,当然一直在while(1)效率肯定不高,后面会使用条件变量来让扫描不那么盲目

void ScanManger::ScanThread(const string& path)
{
	//这个线程就是一直在扫描
	while (1)
	{
		ScanDirectory(path);
	}
}

8.3 扫描管理类的单例化

为什么要单例化?

因为我们的扫描是的时候是需要先去实例化一个对象,那么要是别人也实例化一个对象的话会怎么样呢?

比如:

 那就可能再去创建线程去接着扫描,在这个程序之下,整个系统只需要产生一个对象就行了,多个对象进行扫描肯定是不好的,

而一个类只产生一个对象就是叫做单例化

 我们这里使用的是懒汉模式:

构造私有化,新增获取实例接口
class ScanManager
{
public:
static ScanManager& GetInstance(const string &path);
protected:
ScanManager(const string &path);
ScanManager(ScanManager &);
ScanManager& operator=(const ScanManager&);
private:
//DataManager m_dbmgr;
};
ScanManager& ScanManager::GetInstance(const string &path)
{
static ScanManager _inst(path);
return _inst;
}

九、对sqlite进行静态链接库的使用

我们之前的数据库比较麻烦(20多万行呢),是直接以源码的形式嵌套在我们的系统里面

1、生成静态链接库

我们只需要告诉函数的返回值,名字,参数,也就说告诉别人函数是什么样的功能
但是 函数实现的过程我们不想要告诉别人(可能是提供源码不方便,或者是保护源码)

 

 2、使用生成静态链接库

将程序的头文件 .h + 静态链接库文件 .lib 拷贝至工程 ,也就是说不用源文件了
相当于是工程里只有头文件,源文件没有了,这时候想要运行是不行的
为了 不通过环境设置去解决,想要别人没设置的也可以用
我们这里采用最简单的方式

总结起来就是:
删除 sqlite3.c ,使用 sqlite3.lib 进行替换,然后通过命令引入静态库
#pragma comment(lib, "./sqlite3/sqlite3.lib")

十、新增监控模块

只有扫描而没有监控的话,监控线程就会一直死循环的扫描,文件少的时候还没啥大问题,文件多的话麻烦就大了

我们应该再设置一个监控线程,当本地文件发生变化的时候再去通知扫描线程开始工作

我们监控的就只有三件事情: 文件被删除了,文件重命名,文件增加
要实现这样的功能就需要引入几个API函数
#include<windows.h>
HANDLE FindFirstChangeNotification(
LPCTSTR lpPathName, // pointer to name of directory to watch
BOOL bWatchSubtree, // flag for monitoring directory or
// directory tree
DWORD dwNotifyFilter // filter conditions to watch for
);
BOOL FindNextChangeNotification(
HANDLE hChangeHandle // handle to change notification to signal
);
DWORD WaitForSingleObject(
HANDLE hHandle, // handle to object to wait for
DWORD dwMilliseconds // time-out interval in milliseconds
);

2、在扫描管理类中添加互斥量和条件变量

#include<mutex>
#include<condition_variable>
class ScanManager
{
//...............
mutex m_mutex;
condition_variable m_cond;
};

unique_lock<mutex> lock(m_mutex);  这个锁对象是构造函数加锁,析构函数解锁

3、升级扫描线程和监控线程

升级过后的扫描线程:
void ScanManger::ScanThread(const string& path)
{
	//初始化扫描
	ScanDirectory(path);//防止第一次扫描的时候数据库里没有东西

	while (1)
	{
		unique_lock<mutex> lock(m_mutex);
		m_cond.wait(lock);  //条件阻塞等待,阻塞的时候不占CPU资源
		ScanDirectory(path);
	}
}

升级过后的监控线程:
void ScanManger::WatchThread(const string& path)
{
	                                                   //true表示的是监控子目录
	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);//接下来继续监控 
	}
}

十一、中间逻辑层实现

1、实现拼音全拼和首字母的搜索

要实现拼音搜索的前提是汉字转拼音和汉字转首字母的实现
下面这两个函数就能实现这两个功能
//汉字转拼音
string ChineseConvertPinYinAllSpell(const string &dest_chinese);
//汉字转拼音首字母
string ChineseConvertPinYinInitials(const string &name);

实现转拼音和转首字母之后,还需要在数据库的表中增加两列内容   doc_name_py  和  doc_name_initials
a.对数据库表新增字段
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);
}
b.新增数据
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);
}

2、实现高亮搜索

高亮的原理是分割,把整个字符串分为三个部分:前缀,高亮部分,后缀
使用了函数之后就解决了

颜色打印函数
// 颜色高亮显示一段字符串
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);
}

2.1 实现高亮搜索的关键是实现分割函数

把整个字符串分为三个部分:前缀,高亮部分,后缀
示意图:
分割的时候又分为三种情况
1、原始搜索
这种情况比较简单,直接就能够在原始字符串上面进行分割

 

2、拼音搜索分割
这种情况就比较麻烦了,需要先把原始字符串转换为拼音,然后再查找,最后回到原始字符串上面,对原始字符串动刀

关键是原始字符串上怎么和py字符串上同步,这是个挑战

能够克服双指针移动的问题,就迎来转机了

 

 

3、首字母搜索分割
这个和拼音搜索类似

十二、客户端的实现

1、新增客户端模块sysFrame.h sysFrame.cpp
界面核心技术system

控制台光标位置设置

项目中遇到的问题:

1、高亮显示的时候,
刚开始的时候方向错了,相的是通过system"color  xx"去解决,但是这样的话会把所有要打印内容全部改变颜色,不能一行字当中几个字符的颜色改变,就是

 但是我想的高亮就是应该是 

 这种能让C++这个单独亮起来,别的颜色不变,这时候我就想着怎么能把这个字符串给分割开,就又去想自己写个函数去实现一下,后面边写边上网查,就发现原来有专门的高亮显示函数,就学习了一下这个函数并用上了

 

2、刚开始没想到要有监控线程

当时为了能够实现实时扫描的功能,我想到了要用创建出一个工作线程去专门扫描,但是这个时候做出来是一直while(1),死循环的去扫,开始的时候文件比较少,扫描速度比较快,知道会占CPU资源很多,但是因为好歹是结果对着的就没在意,后面测试的时候文件比较多之后便发现了问题:文件一多扫描一次就需要比较长的时间,而且确实很耗CPU资源,这就是个问题。

刚开始相的解决方案是一个线程慢的话能不能多几个线程去同时扫描数据库,把数据库分成几部分,每个线程负责一个小部分,但是实现的话是思考了一段时间没有成功,后面才想到,我们可以从本质上解决,不让线程一直无脑的扫描,这样时间就大大减少了,然后经过思考和尝试以及上网查资料。。。

后来才想到怎么能利用之前学过的使用懒汉思想,类似于写实拷贝的,就是当本地文件发生改变了扫描线程再去工作,不改变就一直原地等着,让出cpu资源,这时候就想到了条件变量,因为条件变量有通知的功能,从而想到了再创建一个专门监控的线程

对比Everything

对比everything肯定是比不过
我们不是来做产品的,是为了使用数据库,主要是为了 锻炼我们的编程能力
1、Everything优缺点
优点:搜索效率高,不分路劲,搜索的是整个电脑
缺点:不支持拼音搜索,首字母搜搜, 只支持 NTFS 格式的分区
2、自己的项目优缺点
优点:持拼音搜索,首字母搜索,高亮显示
缺点:需要指定路劲搜索,如果数据量非常大,搜索效率可能会比较低下
3、Everything 原理
读取日志文件,不需要扫描目录,实现快速搜索。
            Everything并不扫描整个磁盘,只是读取磁盘上的USN日志,并建立索引,所以速度飞快。         
everything 搜索文件的速度之所以快得令人愤怒,主要原因是利用了 NTFS USNJournal 特性,直 接从
系统的主文件表里读取文件信息。
4Everything-SDK使用
使用开发需要包含文件: Everything.h Everything.c Evreything_ipc.h

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/344983.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

成都女子情人节给东莞男子送巧克力,却被后者典当后换成望京卡牌

一年一度的情人节已经来临&#xff0c;每年的这个时候&#xff0c;都是少男少女们欢庆的节日&#xff0c;因为他们可以借助送礼物&#xff0c;各自表达对另一半的爱慕之情。然而由于中国人的传统观念&#xff0c;一般都是男方给女方送礼物&#xff0c;女方给男方送礼物的就凤毛…

宝塔搭建实战php开源likeadmin通用管理移动端uniapp源码(四)

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 上一期给大家分享了pc端的部署方式&#xff0c;今天来给大家分享uniapp端在本地搭建&#xff0c;与打包发布到宝塔的方法。感兴趣的朋友可以自行下载学习。 技术架构 vscode node16 vue3 uniapp vite types…

PageHelper分页查询

分页查询分页查询的优点所谓分页,就是查询结果数据较多时,采用按页显示的方法,而不是一次性全部显示分页的优点:服务器:一次性查询所有信息,服务器压力大,分页查询服务器压力小客户端:一次性显示所有信息,需要更多流量,加载时间也会更长,分页显示没有这个问题用户体验上:一般最…

Hot 100 | 287. 寻找重复数

LeetCode 287. 寻找重复数 给定一个包含 n 1 个整数的数组 nums &#xff0c;其数字都在 [1, n] 范围内&#xff08;包括 1 和 n&#xff09;&#xff0c;可知至少存在一个重复的整数。假设 nums 只有 一个重复的整数 &#xff0c;返回 这个重复的数 。 你设计的解决方案必须&…

【Spring Cloud Alibaba】(三)OpenFeign扩展点实战 + 源码详解

系列目录 【Spring Cloud Alibaba】&#xff08;一&#xff09;微服务介绍 及 Nacos注册中心实战 【Spring Cloud Alibaba】&#xff08;二&#xff09;微服务调用组件Feign原理实战 本文目录系列目录前言一、Feign扩展点配置二、OpenFeign扩展点配置1. 通过配置文件配置有效范…

二维码数据压缩实践 | 使用python对二维码数据进行压缩 |不乱码,支持中文

当前二维码的应用越来越广泛&#xff0c;包括疫情时期的健康码也是应用二维码的典型案例&#xff0c;最近需要通过一张二维码显示较多文本数据&#xff0c;也就是对二维码数据进行压缩&#xff0c;使用CSDN搜索了半天居然没有能简单使用的代码&#xff0c;很多事例代码解决不了…

机器连接和边缘计算

以一种高效、可扩展的方式进行连接和边缘计算的结合&#xff0c;解决了在工业物联网应用中的机器数据集成问题。 一 边缘计算 边缘计算描述了由中央平台管理的数据分散式处理。边缘计算对于工业物联网而言非常重要。在许多应用程序中&#xff0c;由于数据量非常大&#xff0c;…

C++STL剖析(十)—— 位图(bitset)

文章目录1. 位图的介绍2. 位图的概念3. 位图的实现&#x1f351; 构造函数&#x1f351; 设置指定位&#x1f351; 清除指定位&#x1f351; 获取指定位的状态&#x1f351; 打印函数4. 总结1. 位图的介绍 在介绍位图之前先来看一道面试题吧 给 40 亿个不重复的无符号整数&…

【网络原理2】---TCP协议的格式

传输层重点协议TCP 协议TCP 协议段格式TCP内部的工作机制1. 确认应答2.超时重传TCP 协议 TCP 协议相对于 UDP 是复杂不少的。 在网络编程这里已经讲了 TCP 的特点&#xff1a; 有链接 可靠传输 面向字节流 全双工 可靠传输 是 TCP内部的机制&#xff0c;和编码关系不大&#x…

[oeasy]python0083_十进制数如何存入计算机_八卦纪事_BCD编码_Binary_Coded_Decimal

编码进化 回忆上次内容 上次 研究了 视频终端的 演化 从VT05 到 VT100从 黑底绿字 到 RGB 24位真彩色形成了 VT100选项 从而 将颜色 数字化 了 生活中我们更常用 10个数字 但是 计算机中 用二进制 日常计数的十进制数 是如何存储进计算机的呢?&#x1f914; 从10进制到2进…

Java学习笔记-03(API阶段-2)集合

集合 我们接下来要学习的内容是Java基础中一个很重要的部分&#xff1a;集合 1. Collection接口 1.1 前言 Java语言的java.util包中提供了一些集合类,这些集合类又称之为容器 提到容器不难想到数组,集合类与数组最主要的不同之处是,数组的长度是固定的,集合的长度是可变的&a…

思科网络部署,(0基础)入门实验,超详细

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有收获&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️夕阳下&#xff0c;是最美的绽放&#xff0…

如何让一起打拼的员工有持续的动力

我们的工作都要靠团队的每一个人努力&#xff0c;如何持续让老员工也能有持续的动力完成任务是我们非常重要的管理目标。 方法一&#xff1a;提供稀缺的学习机会。很多企业的培训都是针对新员工或者管理层的&#xff0c;容易让老员工意识不到自己应该学习&#xff0c;接触不到新…

Echarts 雷达图设置拐点大小和形状,tooltip后文字不居中对齐

第017个点击查看专栏目录Echarts的雷达图的拐点大小和形状是可以设置的&#xff0c;在series中设置symbol 相应的属性即可。 使用tooltip的时候&#xff0c;默认状态文字是居中对齐的&#xff0c;不好看。需要在tooltip属性中设置一下&#xff0c;如图所示&#xff0c;效果比较…

记录robosense RS-LIDAR-16使用过程4

一、时隔一个月&#xff0c;再次记录激光雷达的使用&#xff0c;一个月不碰生疏了好多&#xff0c;如鲠在喉&#xff0c;先来个基本操作熟悉一下找找感觉。连接在线雷达&#xff1a;https://github.com/RoboSense-LiDAR/rslidar_sdk/blob/main/doc/howto/06_how_to_decode_onli…

selenium--验证码识别,一文教会你回答面试官

相信大家在日常划水&#xff0c;培训&#xff0c;工作中都遇到这样的问题&#xff0c;验证码怎么处理&#xff1f;也有一些面试官会这么问。这里大致的说说&#xff0c;最常见的处理方式。1、万能验证码&#xff1a;所谓的万能验证码也就是找开发固定一个验证码&#xff0c;比如…

jenkins下配置maven

1. 先在jenkins服务器上安装maven 下载-解压-重命名-启动 [rootVM-0-12-centos local]# wget https://mirrors.aliyun.com/apache/maven/maven-3/3.9.0/binaries/apache-maven-3.9.0-bin.tar.gz [rootVM-0-12-centos local]# tar xf apache-maven-3.9.0-bin.tar.gz [rootVM-0…

嵌入式ARM设计编程(一) 简单数据搬移

文章和代码已归档至【Github仓库&#xff1a;hardware-tutorial】&#xff0c;需要的朋友们自取。或者公众号【AIShareLab】回复 嵌入式 也可获取。 一、实验目的 熟悉实验开发环境&#xff0c;掌握简单ARM汇编指令的使用方法。 二、实验环境 硬件&#xff1a;PC机 软件&am…

【招聘】永善县社会科学界联合会招办公室文秘人员2名

【招聘】永善县社会科学界联合会招办公室文秘人员2名 因工作需要&#xff0c;根据《永善县人力资源和社会保障局关于做好2021年公益性岗位开发管理的通知》(永人社发〔2020〕34号)文件要求&#xff0c;现面向社会公开招聘公益性岗位工作人员&#xff0c;现通告如下&#xff1a…

锐捷(十五)mpls vxn跨域optionc场景

一 实验拓扑二 实验需求ce1和ce2为两个分公司&#xff0c;要求两个分公司之间用mpls vxn 进行通信&#xff0c;组网方式是optionc。三 实验分析optionc在转发平面上有点难理解&#xff0c;有一些关键点需要注意&#xff0c;大家点击链接可以参考我上篇发过的一个文章&#xff1…