某些时候,你磁盘中有一大堆文件出现了重复的前缀,而你又不想一个一个地重命名时……
前言:我为什么想做这个小工具?
前一段时间,我使用Downie这个下载工具下载网站上的视频集的时候,发现它下载到的文件都是以这个格式命名的:
当我想回看这些视频的时候,能帮助我快速定位我想找的视频的文件名,其实只有后面的这一集的名字
,前面的其实都是无用的内容,对于几个零散的文件,我直接上手重命名就好了,但是我面对的是… 这谁顶得住啊!所以,我就想着能不能写一个小工具,帮我完成批量重命名这件繁琐而又重复的工作。
懒汉往往是会推动科技进步的
既然我不想动手,那么我就开始借助程序的力量了。
Coding Time!
在一切开始之前,先来理一下逻辑:
直接上图方便大家理解哈。
首先,编写一个方法,输出一堆花里胡哨的Banner
其实这是我个人编程风格,编写的程序执行时往往是会先输出ASCII艺术字风格Banner 使用http://patorjk.com/software/taag/ 这个网站将英文字母转换为ASCII大艺术字,输入【File Name Prefix Processor】,然后挑一个喜欢的艺术字风格,放在System.out.Println()
语句中进行输出。 如果你想一下一下地逐行输出,可以使用Thread.sleep(300);
延迟一下每一行ASCII艺术字的输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 static void banner () { System.out.println( "███████╗ ██╗ ██╗ ███████╗ \n" + "██╔════╝ ██║ ██║ ██╔════╝ \n" + "█████╗ ██║ ██║ █████╗ \n" + "██╔══╝ ██║ ██║ ██╔══╝ \n" + "██║ ██║ ███████╗ ███████╗ \n" + "╚═╝ ╚═╝ ╚══════╝ ╚══════╝ \n" ); relax(); System.out.println( "███╗ ██╗ █████╗ ███╗ ███╗ ███████╗ \n" + "████╗ ██║ ██╔══██╗ ████╗ ████║ ██╔════╝ \n" + "██╔██╗ ██║ ███████║ ██╔████╔██║ █████╗ \n" + "██║╚██╗██║ ██╔══██║ ██║╚██╔╝██║ ██╔══╝ \n" + "██║ ╚████║ ██║ ██║ ██║ ╚═╝ ██║ ███████╗ \n" + "╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ \n" ); relax(); System.out.println( "██████╗ ██████╗ ███████╗ ███████╗ ██╗ ██╗ ██╗ \n" + "██╔══██╗ ██╔══██╗ ██╔════╝ ██╔════╝ ██║ ╚██╗██╔╝ \n" + "██████╔╝ ██████╔╝ █████╗ █████╗ ██║ ╚███╔╝ \n" + "██╔═══╝ ██╔══██╗ ██╔══╝ ██╔══╝ ██║ ██╔██╗ \n" + "██║ ██║ ██║ ███████╗ ██║ ██║ ██╔╝ ██╗ \n" + "╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ \n" ); relax(); System.out.println( "██████╗ ██████╗ ██████╗ ██████╗ ███████╗ ███████╗ ███████╗ ██████╗ ██████╗ \n" + "██╔══██╗ ██╔══██╗ ██╔═══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ██╔════╝ ██╔═══██╗ ██╔══██╗\n" + "██████╔╝ ██████╔╝ ██║ ██║ ██║ █████╗ ███████╗ ███████╗ ██║ ██║ ██████╔╝\n" + "██╔═══╝ ██╔══██╗ ██║ ██║ ██║ ██╔══╝ ╚════██║ ╚════██║ ██║ ██║ ██╔══██╗\n" + "██║ ██║ ██║ ╚██████╔╝ ╚██████╗ ███████╗ ███████║ ███████║ ╚██████╔╝ ██║ ██║\n" + "╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝\n" ); } static void relax () { try { Thread.sleep(300 ); }catch (Exception e){ e.printStackTrace(); } }
完成之后,编写主类中的main()
方法:
因为我不想让main()
方法显得过于臃肿,所以我将功能性的代码拆分到其他工具类中,main()
方法中只留下了一些提示性的输出语句和调用工具类方法的代码。 (英语渣渣,请不要在意英文提示内容)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public static void main (String[] args) { banner(); System.out.println("请输入要处理文件名前缀的目录路径:\nPlease enter the directory path you want to process the file name prefix:" ); Scanner sc = new Scanner(System.in); String folderPath = sc.nextLine(); FileReader.check(folderPath); System.out.println("即将处理【" + folderPath + "】下的文件...\nWe will process the files in the【" + folderPath + "】folder..." ); System.out.println("请输入你要删除的文件名前缀(Please enter the file name prefix that you want to delete):" ); String prefix = sc.nextLine(); System.out.println("要删除的文件名前缀是(The prefix of the file name you want to delete is):" + prefix); System.out.println("上述文件是通过下列哪个下载器下载的?\nIf the above file(s) was downloaded via a downloader, please indicate which downloader you used." ); System.out.println( "【1】Downie\t\t" +"【n】我没有使用下载器(I did not use any downloader)" ); String choice = sc.nextLine(); switch (choice){ case "1" : PrefixProcessor.downiePrefixProcess(folderPath,prefix); break ; case "n" : PrefixProcessor.commonProcess(folderPath,prefix); break ; default : System.out.println("不支持的操作!即将退出...\nUnsupported!Exiting..." ); System.exit(0 ); } }
编写工具类FileReader
,判断目录是否存在:
如果用户启动程序后,输入一个不存在的路径,程序可能会产生java.io.fileNotFoundException
。 为了避免这种情况,首先对这个问题进行处理,编写check()
方法,参数是用户输入的目录路径,类型为字符串。
1 2 3 4 5 6 7 8 9 10 11 ```java public class FileReader { public static void check(String folderPath){ File f = new File(folderPath); if (!f.exists()) { // 如果这个路径不存在,就结束程序 System.out.println("目录【" + folderPath + "】不存在!"); System.out.println("Path【" + folderPath + "】does not exist!"); System.exit(0); } } }
为什么check(String folderPath)
是静态方法?
因为我懒得在main()
方法中实例化对象,这会增加不必要的语句,并且执行效率也高不了多少,还不如一开始就将这个方法载入到内存中。
编写根据前缀匹配出要处理的文件的方法readFiles()
:
当目录存在,并且用户输入了要处理的前缀时,就应该开始寻找符合条件的文件了。 当然,这个程序处理的是文件,不是文件夹,所以我在程序中设置了忽略文件夹的操作。 在FileReader
类中追加readFiles()
方法,传入两个参数folderPath
(用户输入的路径)和prefix
(要处理的前缀),参数类型均为字符串类型。返回值是ArrayList<File>
数组,用于被其他方法调用并处理这个数组中的文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public static ArrayList<File> readFiles (String folderPath, String prefix) { File f = new File(folderPath); File[] filesInDirectory = f.listFiles(); ArrayList<File> fileListArray = new ArrayList<>(); for (File fs : Objects.requireNonNull(filesInDirectory)) { if (fs.isDirectory()) { System.out.println(fs.getName() + "(是目录,不支持重命名;\n" + fs.getName() + "Is a directory, doesn't support renaming!)" ); } else if (fs.getName().startsWith(prefix)) { System.out.println("找到文件(Found File):" + fs.getName()); fileListArray.add(fs); } } if (fileListArray.size()==0 ){ System.out.println("找不到文件名以【" + prefix +"】开头的文件!即将退出..." ); System.out.println("Cannot find the file whose name starts with 【" + prefix + "】, exiting..." ); System.exit(0 ); } return fileListArray; }
在这里,我先将用户指定的文件夹中的文件全部读出来,然后使用前缀进行文件名匹配,将匹配出来的文件放到ArrayList中,并将这个ArrayList返回,方便这个方法的调用者处理。
编写两个处理规则方法,实现重命名文件:
目前我能接触到的,就两种情况:普通的前缀和Downie下载的文件前缀(带编号的前缀),所以我只编写两个前缀处理方法。 创建PrefixProcessor
类,分别编写commonProcess()
和downiePrefixProcess()
方法,传入两个参数folderPath
(用户输入的路径)和prefix
(要处理的前缀),参数类型均为字符串类型。在这两个方法中调用上面写好的readFiles(String folderPath, String prefix)
方法,并处理其中的文件的文件名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package cn.cf.util;import java.io.File;import java.util.ArrayList;public class PrefixProcessor { public static void downiePrefixProcess (String folderPath, String prefix) { ArrayList<File> fileList = FileReader.readFiles(folderPath, prefix); int length = prefix.length(); for (File file : fileList) { String originName = file.getName(); String newName = originName.substring(length + 9 ); file.renameTo(new File(folderPath + File.separator + newName)); } } public static void commonProcess (String folderPath, String prefix) { ArrayList<File> fileList = FileReader.readFiles(folderPath, prefix); int length = prefix.length(); for (File file : fileList) { String originName = file.getName(); String newName = originName.substring(length); file.renameTo(new File(folderPath + File.separator + newName)); } } }
大家在这里可以注意到有一个坑,在代码第11行和23行,我在文件路径和文件名中间拼接了File.separator
,如果你不这么做,那么被处理的文件将会被移动到当前目录的上一层,并且添加当前文件夹名称作为新的前缀(做了跟没做一样)。
编写结果报告方法,输出DONE !
语句:
和上面输出Banner的方式一样,制作ASCII大艺术字,然后放在System.out.println()
语句中输出就好。
后记
当然,这个程序并不是非常完美的,其中还有很多逻辑漏洞,比如说我没有为输入的前缀进行判空处理等,但是谁会这么无聊,故意用空前缀浪费时间呢(笑) 最后,完整代码和Release可以到相关链接处的GitHub地址里下载,Have fun!XD
相关链接
开源项目地址: 控制台版:https://github.com/chemicalfiber/file-name-prefix-processor https://gitee.com/chemicalfiber/file-name-prefix-processor GUI版:https://github.com/chemicalfiber/file-name-prefix-processor-GUI https://gitee.com/chemicalfiber/file-name-prefix-processor-GUI 编写+测试视频地址(B站): 控制台版:https://www.bilibili.com/video/av177451812 GUI版:https://www.bilibili.com/video/av251059542 编写+测试视频地址(YouTube): 控制台版:https://youtu.be/OEVjdbnTywk GUI版:https://youtu.be/vF-x71zqqh8