一、文件查找并删除
扫描指定⽬录,并找到名称中包含指定字符的所有普通⽂件(不包含⽬录),并且后续询问⽤⼾是否 要删除该⽂件
一个主要的操作就是需要扫描指定目录(递归)
递归函数
- 首先判断是否是目录,若不是,直接返回
- 若是,则列出当前目录的文件名,放到
files
数组中 - 如果
files
是空的,或者files
数组长度为0
,代表没有文件,则直接返回 - 循环遍历
files
数组
- 若此时遍历到的文件是普通文件
- 调用删除文件方法
doDelete
- 若磁石遍历到的仍是目录
- 继续递归
public class Demo15 { //递归目录的方法 private static void scan(File currentFile, String key) { if(!currentFile.isDirectory()){ return; } File[] files = currentFile.listFiles(); if(files == null || files.length == 0){ return; } for(File f : files){ if(f.isFile()){ doDelete(f,key); }else { scan(f,key); } } } }
删除函数
- 文件名中不包含关键字,则直接返回
- 若包含,则提示用户,是否进行删除
- 用户输入
Y/N
进行选择 - 若输入为
Y
或y
,则将此文件删除
private static void doDelete(File f, String key){ if(!f.getName().contains(key)){ return; } Scanner scanner = new Scanner(System.in); System.out.println(f.getAbsolutePath()+"是否确定要删除 Y/N"); String choice = scanner.next(); if(choice.equals("Y") || choice.equals("y")) { f.delete(); } }
完整代码
import java.io.File; import java.util.Scanner; public class Demo15 { //递归目录的方法 private static void scan(File currentFile, String key) { if(!currentFile.isDirectory()){ return; } File[] files = currentFile.listFiles(); if(files == null || files.length == 0){ return; } for(File f : files){ if(f.isFile()){ doDelete(f,key); }else { scan(f,key); } } } private static void doDelete(File f, String key){ if(!f.getName().contains(key)){ return; } Scanner scanner = new Scanner(System.in); System.out.println(f.getAbsolutePath()+"是否确定要删除 Y/N"); String choice = scanner.next(); if(choice.equals("Y") || choice.equals("y")) { f.delete(); } } public static void main(String[] args) { System.out.println("请输入要搜索的路径:"); Scanner scanner = new Scanner(System.in); String rootPath = scanner.next(); File rootFile = new File(rootPath); if(!rootFile.isDirectory()){ System.out.println("输入的路径不存在"); return; } System.out.println("请输入要删除的文件名字的关键字:"); String key = scanner.next(); //进行递归查找 scan(rootFile,key); } }
二、文件复制
进⾏普通⽂件的复制
把一个文件里面的每个字节都读出来,再写入另一个文件中
- 输入源文件路径,并实例出一个
srcFile
对象 - 判断这个对象是否是一个文件,若不是,则返回
- 若是,则继续输入目标文件的路径
- 以这个路径实例化出一个
dextFile
文件 - 得到这个文件所在处的父目录,并判断其是否是一个目录
- 若是,则开始执行复制的过程
- 通过
InputStream
进行读操作,OutputStream
进行写操作
import java.io.*; import java.util.Scanner; public class Demo16 { public static void main(String[] args) throws IOException { Scanner scanner = new Scanner(System.in); System.out.println("请输入源文件路径:"); String srcPath = scanner.next(); File srcFile = new File(srcPath); if(!srcFile.isFile()){ System.out.println("源文件路径有误!"); return; } System.out.println("请输入目标文件路径"); String destPath = scanner.next(); File destFile = new File(destPath); if(!destFile.getParentFile().isDirectory()) { System.out.println("输入的目标文件路径有误!"); return; } try(InputStream inputStream = new FileInputStream(srcFile); OutputStream outputStream = new FileOutputStream(destFile)){ while (true) { byte[] buffer = new byte[1024]; int n = inputStream.read(buffer); if(n == -1){ break; } outputStream.write(buffer,0,n); } }catch (IOException e){ e.printStackTrace(); } } }
try()
里面可以写多个对象,多个对象的构造过程使用 ; 分隔就可以了- 写入的时候,不能直接
write(buffer)
,因为前面读操作不一定能把buffer
填满,若直接写入buffer
,就把没有用到的空间也写入了,不太合适
- 也许
buffer
只填了100
个空间,剩下924
个空间都是0
,写入就没有意义 - 此时我们就需要指定空间写入
[0, n]
三、递归遍历目录查找文件
扫描指定⽬录,并找到名称或者内容中包含指定字符的所有普通⽂件(不包含⽬录) 注意:我们现在的⽅案性能较差,所以尽量不要在太复杂的⽬录下或者⼤⽂件下实验
import java.io.*; import java.util.Scanner; public class Demo17 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("请输入要搜索的路径:"); String rootPath = scanner.next(); File rootFile = new File(rootPath); if(!rootFile.isDirectory()){ System.out.println("要搜索的路径有误!"); return; } System.out.println("请输入要搜索的查询词:"); String key = scanner.next(); //进行递归查找 scan(rootFile,key); } private static void scan(File rootFile, String key) { if(!rootFile.isDirectory()) { return; } File[] files = rootFile.listFiles(); if(files.length == 0 || files == null) { return; } for(File f : files) { if(f.isFile()){ //进行后续的查询操作 doSearch(f,key); }else{ scan(f,key); } } } private static void doSearch(File f, String key) { //打开文件,读取文件内容,并判定文件内容是否包含 key StringBuilder stringBuilder = new StringBuilder(); //因为不考虑二进制,直接按照文本的方式来处理,就直接用 Reader try(Reader reader = new FileReader(f)){ char[] buffer = new char[1024]; while(true) { int n = reader.read(buffer); if(n == -1){ break; } String s = new String(buffer,0,n); stringBuilder.append(s); } }catch (IOException e){ e.printStackTrace(); } if(stringBuilder.indexOf(key) == -1) { //未找到 return; } //找到了 System.out.println("找到匹配的文件" + f.getAbsolutePath()); } }
- 这个代码逻辑效率很低,每次查询都会涉及到大量的硬盘
IO
操作,因为每次判定都要将硬盘里面的所有文件都读一遍。尤其是遇到硬盘上有些大的文件 - 这种思路不能适应频繁查询场景,也不能适应目录中文件数目特别多,特别大的场景
咱们搜索引擎中,进行搜索的过程,也就是在文件中查找内容是否被包含的过程
搜索出来的结果其实就是一些 HTML
文件,这些 HTML
文件里面一定是包含你的查询词(或者和你的查询词有关的)
搜索引擎每次搜索都是在数以十亿,数以百亿的 HTMl
中,找到几十万,几百万个结果
搜索引擎这样的场景,不能通过上述“遍历文件”方式实现
- 其中最核心的优化,是引入了神奇的数据结构——倒排索引
- 提前把所有的文件,里面的内容都分析好,分析出一个文件中,包含哪些词,再基于这个结果,得到另一份数据,每个词都在哪些文件中包含着
- 之后就是一个查询哈希表的过程
- 主要的难点就是这个哈希表如何来
在未来实际工作中,也会用到一些“自定制的搜索引擎”
比如,我们自己的代码中,产生大量的日志,把这些日志导入到自己搭建的搜索引擎中,从而快速查找
- 用到一些业界成熟的方案,比如 ES(倒排索引原理) 这种