超级搜索everything项目

超级搜索项目

前言

都有过使用Windows搜索的经历吧,开局一张图,搜索真是慢,对于学习计算机的孩子,由于磁盘中存放着大量的文件,或许有些文件搜索的时间还算快吧,小咲猜想到,可是事实的真相是什么呢?我们一起眼见为实,有请小咲。好,欢迎来到是真的吗?真假实验室,事实的结果让你眼见为实。【哒哒哒一段柯南探案的音乐】从22:38分一直加载了十分钟左右才完全加载完,那么真相只有一个,真的很慢啊。而且即便你反复搜索同一个关键字,还是一样的慢慢慢,是时候展示真正的技术了。
超级搜索everything项目
超级搜索everything项目

设计背景

不光上面的时间问题这一种情况,在大多数操作系统中,都有着文件搜索的功能,这项功能对于那些经常忘记重要文件存放路径的小咲来说,那可真的是工作和生活中必不可少的功能,最常用的就是everything。在家里所有人的电脑里统统装了一个这样的小程序。

在 Windows10 中,桌面底部状态栏的左下角就有个人助理小娜有时小咲也能通过命令搜索一些小工具,是真的非常方便。不过呢最近也遇到了如下的问题( •̀ ω •́ )y。

超级搜索everything项目

网上对于搜索这么慢的情况,吐嘈声不断,既然我们还在用Windows,不如摆脱微软官方的搜索功能的束缚,自己动手,丰衣足食,许多大佬开发了很多功能强大的搜索神器,例如:Everything。

Everything是voidtools开发的一款文件搜索工具,官网描述为“基于名称实时定位文件和目录(Locate files and folders by name instantly)”。基于NTFS文件系统的USN Journal(Update SequenceNumber Journal),用操作系统记录的文件操作日志来进行搜索,特点是效率高,速度快,但是具有一定的局限性,只能用于NTFS文件系统。

设计思路

于是我开始模仿这款搜索神器也做了一个“低配版”的搜索器。但是如何设计呢?

万事不妙问“度娘”,百度百科来回答,首先查询资料要解决设计这一关的问题,首先要去设计一个基本框架流程图。根据下面的问题,得出基本思路。

1.2 “Everything”建立数据库需要多长时间?

“Everything”搜索只基于文件和文件夹的名称,所以它创建数据库很快。一个刚安装完的Windows XP SP2系统(约20,000份文件),需要一秒钟。索引一百万份文件则需要一分钟。

1.4 “Everything”是不是非常占用系统资源?

不,“Everything”使用非常少的系统资源。一个刚安装完的Windows XP SP2系统(约20,000份文件)需要占用3-5 mb内存和不到1 mb的硬盘空间。一百万份文件大概需要45 mb内存和5 mb硬盘空间。

推论:根据以上问题可以考虑使用一个轻量级的数据库,使用SQLite即可。

1.3 “Everything”能否搜索文件内容?

可以,“Everything” 可以通过搜索函数 content: 来搜索文件内容。文件内容未被索引时,搜索内容将会很慢。

推论:暂时不考虑处理这项功能,因为文件内容都未加入索引,搜索内容会很慢。后续如果需要提高可以尝试去处理。

1.5 “Everything”能否监视文件系统更改?

是的,“Everything”能够监视文件系统改变。文件和文件夹名称的改变会实时地反映到“Everything”数据库。

推论:暂时不实现,实时刷新这个技术可以实现,但是对于CPU性能开销可能比较大,暂时忽略。

1.7 “Everything”不在运行的时候,它还能否监视文件系统更改?

即使在“Everything”没有运行的情况下,更改文件系统也不会有什么问题,因为 “Everything”在每次启动的时候会更新数据库。

推论:需要做到上一次将内容保存到数据库,下一次还需要更新数据库,那么如何使得处理速度加快呢,清空数据库再遍历,显然不是好的方法。那么就举一个实例:某根目录重命名了,下面的子文件都没变,那么你尽量要做到只需要修改根目录名。

由此诞生了基本的设计思路与计划……

项目的核心功能

作为一款搜索器,那最基本就要能在本地中找到与目标文件名相匹配的文件,但是大多数人都不喜欢输入太精确的文件信息,因为大多数人们也许只能记得目标文件名中某些关键词,所以要解决这个问题,就得进行”模糊匹配“,将本地的文件信息保存进 SQLite 数据库,然后和用户输入的目标文件的信息进行匹配,实现文件名搜索、拼音全拼搜索、拼音首字母搜索。为了实现这个多功能搜索,我选择引入 pinyin4j 的 Java 类库,可以将用户输入的文件信息转换成包含全拼、首字母等的字符数组,方便后端进行后续功能的实现。

本项目使用实时的本地文件进行搜索,保存文件信息以便于提高搜索效率。

技术栈介绍

SQLite介绍

SQLite是一款轻量级的嵌入式内存数据库(嵌入在进程中,运行在内存中的数据库),使用 ANSIC 编写的,并提供了简单和易于使用的 API。 一个完整的 SQLite 数据库是存储在一个单一的跨平台的磁盘文件。 非常小,是轻量级的,完全配置时小于 400KiB,省略可选功能配置时小于250KiB。 无需安装、配置及管理。 完全兼容 ACID 事务,允许从多个进程或线程安全访问。 支持多种开发语言,C, C++, PHP, Perl, Java, C#,Python, Ruby等 支持 SQL92(SQL2)标准的大多数查询语言的功能。

Pinyin4j介绍

是一个Java的内库,提供对中文汉字到拼音的转换 存在多音字的情况,根据一个字符可以获取多个字符串。

String[] pinyins = PinyinHelper.toHanyuPinyinStringArray('长');

输出: [zhang3, chang2] 可以配置输出格式,包括大小写、是否带音调(默认带)、是否使用v(如绿的拼音lv)

JavaFX介绍

Java客户端UI库:JavaFX是一个强大的图形和多媒体处理工具包集合,它允许开发者来设计、创 建、测试、调试和部署富客户端程序,并且和Java一样跨平台。 提供了丰富的客户端组件:面板、按钮、文本框、表格等,还提供了各类事件、动画效果支持。 提供了Web组件支持:HTML5的支持、CSS样式支持,JavaScript脚本支持。 还提供了Scene Builder 程序,来支持拖拽式界面开发。 Java原有Swing、AWT客户端UI工具包,JavaFX 系统界面样式跟美观、系统架构对开发更友好、便捷。

项目的开发环境

  • Windows10
  • IntelliJ IDEA
  • Maven

项目的核心技术

  1. 使用 JavaFX 制作前端用户界面。
  2. 使用多线程高效地完成本地文件的遍历扫描任务。
  3. 使用轻量级数据库 SQLite 完成本地文件信息的存储任务。
  4. 使用 pinyin4j 类库完成本地文件信息的中文汉字到汉语拼音的转换。

项目的主要流程图

整体流程

超级搜索everything项目

文件对比超级搜索everything项目

项目的模块组成

文件扫描任务

为了提高文件扫描的效率,引入了多线程功能。在目标路径下扫描,若遇到子文件夹便创建新线程进入子文件夹扫描子文件夹下的文件信息,以此递归来实现高效的文件扫描任务。
有时候我们会遇到这种情况:第一次选择了一个错误的扫描路径,第二次才选择到争取的扫描路径。大多数人当然不愿意再等待程序扫描完第一次错误的路径后,再开始第二次正确路径的扫描。因此,我将这种情况发生时,会让程序将第一次扫描任务的线程全部阻塞掉,后来进入的线程也拒绝掉,等全部线程都停止后再开始第二次的扫描。这样就大大缩短了等第一次扫描停下来的时间,提高了扫描的效率。

文件信息存储

为了达到轻量级的目的,选择了 SQLite 作为文件信息存储的数据库。当文件扫描任务完成时,接口回调机制会提醒文件信息存储任务开始,将扫描到的文件信息按照我定义的标准格式存储进 SQLite 数据库中。此时会遇到一个主要的问题,就是本地信息和数据库中的信息不匹配,这时候就要将数据库中的文件信息与本地进行同步。主要分为以下两种情况:
本地有,数据库没有,这种情况便在数据库中新增此条文件信息。
数据库有,本地没有,这种情况便将数据库中的此条文件信息删除(若该条文件信息是文件夹,则删除该文件夹下所有的文件信息)。

文件信息搜索

通过引入 pinyin4j 的类库,我可以将用户输入的目标文件的信息转换成汉语拼音全拼及首字母的字符数组,再到已经存放了本地文件信息的 SQLite 数据库中通过 JDBC 操作来搜索目标文件信息。

文件信息展示

通过 JavaFX 创建用户界面,并将文件信息罗列在前端窗口上。
当目标扫描路径改变时,清空界面列表,并重新获取新扫描路径的文件信息。

代码中包与类的功能描述

基本架构

app 包

  • Controller 类
  • FileMeta 类

task 包

  • DBInit 类
  • FileSave 类
  • FileScanner 类
  • FileSearch 类
  • ScanCallBack 接口

util 包

  • DBUtil 类
  • PinyinUtil 类
  • Util 类

Main 类(没什么说明的,因为不需要守护线程的说,(_)):调用 JavaFX 创建用户界面以及项目主入口。


具体功能说明

在 app 包下

Controller 类:
提供前端用户界面的实现。
提供选择文件目录的功能。
提供用户界面列表的刷新功能。
FileMeta 类:
创建一个类用来存储扫描文件的信息,属性皆是数据库中的字段。
提供一个构造方法和判断是否是目录的方法。

在 task 包下

DBInit 类:
提供两个方法,分别为 readSQL() 和 Init()。readSQL() 通过类加载器获取流,得到数据库初始化的 sql 语句,并送到 Init() 方法的Statement 中,完成数据库的初始化工作。
FileScanner 类:
创建线程池,使用多个线程在进行本地文件的递归遍历,其中从根路径开始,每当遇到子文件为目录时,便创建新的线程去遍历子目录。
采用接口回调机制,当 FileScanner 类完成本地文件扫描任务后,通知 FileSave 类将扫描到的文件信息保存进 SQLite 数据库。
最后提供关闭线程池的功能。应用场景为:第一步:选择了一个较大的目录,扫描开始执行。第二步:又选择了一个较小的目录。此时我们希望能够将第一次选择的大目录的扫描任务终止掉,进而让全部计算资源进行新的目录的扫描任务,所以采用 pool.shutdownNow() 方法,将第一次的扫描线程全部阻塞,并且拒绝掉后进来的线程,停止后进行第二个小的目录的遍历扫描。
FileSave 类:
主要提供对数据库文件信息的增、删、改、查功能。作为 FileScanner 类的上级,当下级完成扫描任务后,得到扫描完成的通知,然后将下级扫描的文件信息结果进行保存到数据库,并且获取到子文件和子文件夹的信息,若查询到子文件和子文件夹的信息和数据库里存储的信息不匹配,分为两种情况:若数据库有,但本地没有,则将数据库里的信息删除;若本地有,但数据库没有,则将本地的信息添加进数据库。
增删改查皆是基于 JDBC 的操作。
FileSearch 类:
提供在数据库中对目标扫描路径下的子文件及子目录的查找功能。
ScanCallBack 接口:
由 FileSave 类实现,简单的提供一个 callBack() 方法,当 FileScanner 类完成扫描后进行反馈。

在 util 包下

DBUtil 类:
提供了使用单例模式获取数据库连接池的功能。
获取数据库 URL 并得到数据库连接。
提供关闭数据库功能。
PinyinUtil 类:
提供设置中文字符格式。
提供将汉语拼音格式化为统一格式。
提供通过文件名获取目标文件全拼及拼音首字母的功能。
Util 类:
提供设置日期时间格式。
提供将日期解析为中文描述。
提供转换文件大小为中文描述的功能。

在main包下

Main 类:调用 JavaFX 创建用户界面以及项目主入口。

项目中问题的解决与优化:

问题:如果前一次选择目录,加载还未完成,就再选择目录,会造成前一次并未停止执行后一次与前一次并发执行的bug。

优化:通过引入ThreadPoolExecutor的拒绝策略,这里写了一篇博客,可以基本了解一下四种策略的不同,项目中尝试使用了策略1/2,这两种好像都可以,问题不大,所以最后用的是默认的Abort策略即可。

项目无法处理的部分

1、对于内容无法搜索,因为考虑到未添加索引的关系,这里技术上还不足以支持,可以通过后续学习索引知识,加上此功能。

2、项目由于内部代码递归访问,导致不能立即暂停,虽然已经尝试使用了ThreadPoolExecutor的拒绝策略,但是还是不能做到立刻停止的效果。

使用的设计模式——单例模式

你可以参考小咲的一篇关于双重校验锁的单例模式。

https://blog.csdn.net/weixin_43914278/article/details/104451055

项目效果演示超级搜索everything项目

超级搜索everything项目

项目总结

项目优点:使用多线程来进行文件遍历,提高了效率。

项目缺点:中文汉字多音字没有进行排列组合,只能支持多音字拼音的一种组合搜索。

项目扩展:我们还能将该项目写的更完善,可以往以下几个发展方向走: 1. 多音字支持 2. 将项目打包成exe安装文件

项目参考资料

SQLite Home Page
Pinyin4j、http://pinyin4j.sourceforge.net/
JavaFX官网、FX China、JavaFX 教程

鸣谢

就用一个写的MATLAB的心形曲线做结尾吧,想要了解的,https://blog.csdn.net/weixin_43914278/article/details/104445507

超级搜索everything项目