某些时候,你磁盘中有一大堆文件出现了重复的前缀,而你又不想一个一个地重命名时……
           
      前一段时间,我使用Downie这个下载工具下载网站上的视频集的时候,发现它下载到的文件都是以这个格式命名的:
当我想回看这些视频的时候,能帮助我快速定位我想找的视频的文件名,其实只有后面的这一集的名字,前面的其实都是无用的内容,对于几个零散的文件,我直接上手重命名就好了,但是我面对的是…所以,我就想着能不能写一个小工具,帮我完成批量重命名这件繁琐而又重复的工作。 
        
           
      既然我不想动手,那么我就开始借助程序的力量了。
        
           
      
        
           
      直接上图方便大家理解哈。
        
           
      其实这是我个人编程风格,编写的程序执行时往往是会先输出ASCII艺术字风格Bannerhttp://patorjk.com/software/taag/ 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()语句中输出就好。
        
           
      当然,这个程序并不是非常完美的,其中还有很多逻辑漏洞,比如说我没有为输入的前缀进行判空处理等,但是谁会这么无聊,故意用空前缀浪费时间呢(笑)
        
           
      开源项目地址:https://github.com/chemicalfiber/file-name-prefix-processor https://gitee.com/chemicalfiber/file-name-prefix-processor https://github.com/chemicalfiber/file-name-prefix-processor-GUI https://gitee.com/chemicalfiber/file-name-prefix-processor-GUI https://www.bilibili.com/video/av177451812 https://www.bilibili.com/video/av251059542 https://youtu.be/OEVjdbnTywk https://youtu.be/vF-x71zqqh8