美文网首页
Java实战开发(4)——命令行工具(类似cmd指令)

Java实战开发(4)——命令行工具(类似cmd指令)

作者: 让时间走12138 | 来源:发表于2020-04-20 22:13 被阅读0次

    本节内容

    1.项目功能和结构分析

    2.定义接口统一回调接口

    3.解析指令&自定义异常

    4.cd和cd..指令实现

    5.rm和ls指令实现

    6.copy指令实现

    一、项目功能和结构分析

    1.具体功能:首先程序运行起来会提示对应的路径,①输入ls就可以查看当前的所有内容 ②如果想要进入某一个目录里面,就输入cd+目录名称 ③创建一个新目录:输入mkdir+目录名称 ④删除这个目录:输入rf+目录名称 ⑤返回桌面:输入cd..
    2.结构分析
    ①首先有一个CommendTool类用来模拟界面,需要记录当前目录,还有一个start()方法,让这个界面启动起来。
    ②然后有一个CommendOperation类来等待用户输入,在这里面有一个readCommend()方法来读取指令,然后解析相应的操作(比如增加目录,删除目录等),之后再把这个结果回调给CommendTool
    ③为了方便前面两个类的通信,我们可以统一一个接口,在这个接口里面包含一些方法(List:将它列出来, mkdir:创建目录,copy:拷贝文件或文件夹,remove:删除,cd_to_child:跳转到子目录,cd_to_parent:返回到上一级)
    ④一旦readCommend()将命令解析出来,CommendTool就可以通过接口来实现相应的方法
    ⑤iCmd接口规定我们有哪些指令
    ⑥如果调用ls方法,将所有的目录展示出来,那我们就需要一个FileManager类用来管理文件的所有操作,使用单例设计模式

    二、定义接口统一回调接口

    1.首先定义一个CommendTool类,在类里面,我们要定义一个目录,它可以默认启动起来显示操作、,还要定义一个变量记录当前操作的目录路径,调用构造方法,让这个变量指向那个默认路径
    public class CommendTool {
        默认启动起来显示操作的目录
        private static final String DESK_TOP="C:\\Users\\86178\\Desktop";
        记录当前操作的目录路径
        private StringBuilder currentDirPath;
        public CommendTool() {
           currentDirPath=DESK_TOP;
        }
        启动命令行工具
      public void start(){
       System.out.println("欢迎使用计算机鬼才定制版命令行工具");
      }
    }
    
    2.创建一个CommendOperation类,接收用户输入和解析输入类型
    public class CommendOperation {
        接收用户输入的指令
        public void readCommend(){
    
        }
    }
    
    这个时候可以补充start方法,首先定义一个CommendOperation类的对象,然后通过这个对象调用readCommend方法,以便进行相应的操作
    public void start(){
            创建读取指令的对象
          CommendOperation operation= new CommendOperation();
          System.out.println("欢迎使用计算机鬼才定制版命令行工具");
          用户输入指令
          operation.readCommend();
      }
    
    3.在接收了指令之后我们就要解析指令,那么在解析指令之前我们需要一个接口包含所有的指令。因为接口默认属性都为public static final类型,所以不用特地加上这个public static final。
    public interface ICmd {
        String LS= "ls";        列出当前目录内容
        String MKDIR="mkdir";   创建目录
        String COPY="copy";     拷贝
        String RM="rm";         删除
        String CD="cd";         进入一个目录
        String CDP="cd..";      进入上一层目录
    }
    
    4.定义一个ICommend接口,里面包含所有操作对应的抽象方法。
    public interface ICommend {
       1.列出当前目录的所有内容  名字+size
       boolean list();
       2.创建一个目录
       boolean mkdir(String path);
       3.将src的文件复制到des的位置
       boolean copy(String src,String des);
       4.删除文件/目录
       boolean remove(String path);
       5.切换当前目录到子目录
       boolean cd_to_child(String path);
       6.切换到上一层目录
       boolean cd_to_parent();
    }
    
    5.然后让CommendTool类继承ICommend接口,并添加相应的抽象方法。
     @Override
        public boolean list() {
            return false;
        }
    
        @Override
        public boolean mkdir(String path) {
            return false;
        }
    
        @Override
        public boolean copy(String src, String des) {
            return false;
        }
    
        @Override
        public boolean remove(String path) {
            return false;
        }
    
        @Override
        public boolean cd_to_child(String path) {
            return false;
        }
    
        @Override
        public boolean cd_to_parent() {
            return false;
        }
    
    先添加这些方法,后面再一步步慢慢实现。这个时候我们还要修改一下CommendOperation类。
    6.回调给CommendTool的应该是ICommend类的一个对象,所以要创建一个ICommend的对象,并将其作为readCommend的一个参数。解析指令最好是另外写一个函数包裹起来,使用的时候调用该方法即可。
    public class CommendOperation {
        1.回调对象
        private ICommend listener;
        2.获取输入信息
         private   Scanner mScanner;
         public CommendOperation(){
             mScanner=new Scanner(System.in);
         }
        3.接收用户输入的指令
        public void readCommend(ICommend listener){
         this.listener=listener;
           4. 接收指令
            String commend=mScanner.nextLine();
            5. 解析指令
            parseCommend(commend);
        }
        public void parseCommend(String cmd){
    
        }
    }
    

    三、解析指令&自定义异常

    1.先补充一下readcommend函数调用里的参数
     operation.readCommend(this);
    
    2.在ICmd接口里面添加一个指令数组,里面包含所有正确的指令
     String[] COMMONDS=new String[]{LS,MKDIR,COPY,RM,CD,CDP};
    
    3.接着我们逐步实现解析指令的函数,我们可以将指令用空格符分隔开,然后获取用户的指令
        将指令以空格为分隔符分开
           String[] compoents=commond.split(" ");
         获取用户指令
           String cmd= compoents[0];
    
    4.为了判断指令存不存在,我们需要调用contains方法,但是这个是List的方法,所以我们要把前面定义的普通数组转化为List数组。在 CommendOperation类里面定义数组,在这个类的构造方法里面进行转换
       //  保存所有指令
         private List<String> commonts;
          public CommendOperation(){
             mScanner=new Scanner(System.in);
            // 将普通的Array类型转换为List
             commonts= Arrays.asList(ICmd.COMMONDS);
         }
    
    5.如果指令不存在,我们就要抛出相应的异常了,但因为这个异常不是系统自带的,所以我们要自己定义一个异常。自定义异常里面添加一个内部类,然后实现这个内部类的构造方法
    public class GeniusException {
        //指令不存在
        static class CommondNotExistException extends Exception{
            public CommondNotExistException(String s) {
                super(s);
            }
        }
    }
    
    6.因为会抛出异常,所以前面的parseCommend()方法和readCommend()方法都需要继承这个异常
     public void readCommend(ICommend listener) throws 
    GeniusException.CommondNotExistException{...内部内容见上}
    
    public void parseCommend(String commond) throws 
    GeniusException.CommondNotExistException{...内部内容见上}
    
    7.那么在CommendTool 类里面的start()方法里接收用户的输入就需要添加一个try-catch方法
     //用户输入指令
          try {
              operation.readCommend(this);
          } catch (GeniusException.CommondNotExistException e) {
             System.out.println(e.getMessage());
          }
    
    8.然后我们接着完成解析指令的函数,如果指令不存在就抛出异常
         判断指令是否存在
        if(!commonts.contains(cmd)){
           1. 输入指令不存在
           2.抛出异常
            throw new GeniusException.CommondNotExistException("指令不存在");
        }
    

    四、cd和cd..指令实现

    1.在start方法里面我们要显示一下目录替换,可以另外写一个showparent方法,在start里面调用即可。
          显示目录替换
          showParent();
    
    2.想要显示上一个目录的名称,我们可以通过\\ 获取最后一个字符串的索引值,然后通过substring方法获取这个内容,然后再将它输出
     private void showParent(){
            1.获取最后一个\\的index
          int start= currentDirPath.lastIndexOf("\\");
            2.获取最后的内容
        String parent=  currentDirPath.substring(start);
            3.输出提示内容
          System.out.print(parent+"#");
      }
    
    3.因为显示这个内容以及抛出异常是需要不断重复进行的,所以我们可以用一个while循环将其包裹起来
     while (true) {
              //显示目录替换
              showParent();
              //用户输入指令
              try {
                  operation.readCommend(this);
              } catch (GeniusException.CommondNotExistException e) {
                  System.out.println(e.getMessage());
              }
          }
      }
    
    4.然后我们可以开始解析指令,用switch语句来完成各个部分的功能。首先是ICmd.CD,然后判断它的compoents.length!=2(这个就是解析的语句不等于2,也就是要么没有要进入的目录,要么有多个目录,都不符合我们的要求),那么就抛出异常。所以我们还要自己另外定义一个异常
    static class CommondArgumentException extends Exception{
            public CommondArgumentException(String s) {
                super(s);
            }
        }
    
    5.然后让前面的函数都继承这个异常
    public void readCommend(ICommend listener) throws
    GeniusException.CommondNotExistException,
    CommendTool.GeniusException.CommondArgumentException{...}
    public void parseCommend(String commond) throws
    GeniusException.CommondNotExistException,
    GeniusException.CommondArgumentException {...}
    
    6.接着可以实现switch里的ICmd.CD板块里的内容
     switch (cmd){
                case ICmd.CD:
                    if(compoents.length!=2){
                   throw new GeniusException.CommondArgumentException("cd 参数不正确");
                    }
                   listener.cd_to_child(compoents[1]);
                    break;
            }
    
    7.然后实现cd_to_child函数
    @Override
        public boolean cd_to_child(String path) {
          currentDirPath= currentDirPath.append("\\"+path);
            return false;
        }
    
    8.接着实现switch里的CDP,compoents.length!=1表明输多了参数,不符合我们只有一个命令的这种可能
            case ICmd.CDP:
                    if(compoents.length!=1){
                        throw new GeniusException.CommondArgumentException("cd.. 不需要参数");
                    }
                    listener.cd_to_parent();
                    break;
    
    9.然后实现cd_to_parent函数,因为是返回上一个目录,所以我们删除一部分后缀即可,那么就调用delete函数,需要输入删除的部分的首尾索引值
     @Override
        public boolean cd_to_parent() {
          int start= currentDirPath.toString().lastIndexOf("\\");
          int end= currentDirPath.length();
          currentDirPath.delete(start,end);
            return false;
        }
    
    10.然后实现Switch里的MKDIR,因为创建一个目录,所以需要两个解析式,前面是选择的操作,后面是创建的目录名称
     case ICmd.MKDIR:
                    if(compoents.length!=2){
                        throw new GeniusException.CommondArgumentException("cd 参数不正确");
                    }
                    listener.mkdir(compoents[1]);
                    break;
    
    11.然后实现CommendTool里的mkdir函数
     @Override
        public boolean mkdir(String path) {
            //拼接完整路径
            String dirPath=currentDirPath.toString()+"\\"+path;
          FileManager.getInstance().mkdir(dirPath);
            return false;
        }
    
    12.因为要创建新目录,所以不得不再创建一个FileManager类,然后采用单例设计模式
    public class FileManager {
        private static FileManager manager;
        private FileManager(){}
    
        public static FileManager getInstance(){
            if(manager==null){
                synchronized (FileManager.class){
                  if(manager==null){
                      manager =new FileManager();
                  }
                }
            }
            return manager;
        }
    
        public boolean mkdir(String path){
            File file =new File(path);
            if(file.exists()){
                return false;
            }
            //mkdir要求路径中的目录都存在
            //mkdirs 如果路径中的目录不存在,也会自动创建
            return   file.mkdir();
        }
    }
    

    五、rm和ls指令实现

    1.实现switch里面的RM方法,因为有两个解析式,一个是操作,还有一个是要删除的目录的名字,所以判断length是否=2
    case ICmd.RM:
                    if(compoents.length!=2){
                        throw new GeniusException.CommondArgumentException("rm 参数不正确");
                    }
                 listener.remove(compoents[1]);
                    break;
    
    2.然后实现CommendTool里的remove函数,先拼接路径再删除
    @Override
        public boolean remove(String path) {
            String dirPath=currentDirPath.toString()+"\\"+path;
             FileManager.getInstance().remove(dirPath);
            return false;
        }
    
    3.在FileManager类里面添加remove函数,关于文件的操作都需要在FileManager里面添加相应的方法。如果目录存在才需要删除,否则就返回false
    public boolean remove(String path){
            File file =new File(path);
            if(file.exists()){
               return file.delete();
            }
            return false;
        }
    
    4.实现switch里的LS方法,它也只需要一个解析式即可,不需要参数
    case ICmd.LS:
                    if(compoents.length!=1){
                        throw new GeniusException.CommondArgumentException("ls 不需要参数");
                    }
                    listener.list();
    
    5.然后实现CommendTool里的list方法,如果想要让显示的内容对齐,就可以添加一个for循环,那个50为空格数
     @Override
        public boolean list() {
           File[] files= FileManager.getInstance().list(currentDirPath.toString());
           for(File file:files){
               //获取文件名
               String name= file.getName();
               //获取文件长度
               long size= file.length();
               long kb= size/1024;
               long by=size%1024;
              System.out.print(name);
              for(int i=0;i<50-name.length();i++){
                   System.out.print(" ");
               }
               System.out.println(name+" "+kb+"."+by+"kb");
           }
            return false;
        }
    
    6.因为需要展示所有文件目录,所以需要在FilManager添加这个list方法
    public File[] list(String path){
            File file =new File(path);
            if(!file.exists()){
                return null;
            }
           return file.listFiles(new FilenameFilter() {
               @Override
               public boolean accept(File file, String s) {
                   if(s.startsWith(".")) {
                       return false;
                   }
                   return true;
               }
           });
        }
    
    因为我们不想展示以“.”开头的隐藏文件,所以我们获取文件的时候需要过滤一下,所以要new 一个FilenameFilter

    六、copy指令实现

    1.实现switch里的COPY操作,因为有选择操作,还有原文件名以及目标文件名,所以length与3比较
     case ICmd.COPY:
                    if(compoents.length!=3){
                        throw new GeniusException.CommondArgumentException("copy 参数不正确");
                    }
                    listener.copy(compoents[1],compoents[2]);
                    break;
    
    2.实现CommendTool里的copy函数
     @Override
        public boolean copy(String src, String des) {
            String srcPath=currentDirPath.toString()+"\\"+src;
            String desPath=currentDirPath.toString()+"\\"+des;
            FileManager.getInstance().copy(srcPath,desPath);
            return false;
        }
    
    3.因为涉及到文件的拷贝,那么就需要在FileManager里面添加这个copy方法
    public boolean copy(String src,String des){
         File srcfile= new File(src);
         File desfile= new File(des);
         //判断文件是否存在
            if(!srcfile.exists()||!desfile.exists()){
                return false;
            }
            //判断是不是文件
            if(srcfile.isFile()){
                //直接拷贝文件
                copyFile(src,des);
            }else {
            //创建当前目录
                desfile.mkdir();
                //需要将原目录的所有内容copy到des目录
                for (File file:srcfile.listFiles()){
                    copy(file.getAbsolutePath(),des+"\\"+file.getName());
                }
            }
            return true;
        }
    
    拷贝文件函数见下,使用字节数组会快一点,最后要用flush刷新一下。把创建输入流的那几行放进try的括号里面这样就不需要另外单独close了
    public void copyFile(String src,String des){
            try (//创建输入流
                 FileInputStream fis= new FileInputStream(src);
                 BufferedInputStream bis= new BufferedInputStream(fis);
                 //创建输出流
                 FileOutputStream fos =new FileOutputStream(des);
                 BufferedOutputStream bos= new BufferedOutputStream(fos);){
                //创建Buffer
                byte[] Buffer=new byte[1024];
                int len=0;
                while ((len=bis.read())!=-1){
                    bos.write(Buffer);
                }
                bos.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    欧克,以上就是我们今天的全部内容,再见!
    以下为全部的源代码
    public interface ICmd {
        String LS= "ls";//列出当前目录内容
        String MKDIR="mkdir";//创建目录
        String COPY="copy";//拷贝
        String RM="rm";//删除
        String CD="cd";//进入一个目录
        String CDP="cd..";//进入上一层目录
    
        String[] COMMONDS=new String[]{LS,MKDIR,COPY,RM,CD,CDP};
    }
    
    public interface ICommend {
        //列出当前目录的所有内容+  名字+size
       boolean list();
       //创建一个目录
       boolean mkdir(String path);
       //将src的文件复制到des的位置
       boolean copy(String src,String des);
       //删除文件/目录
       boolean remove(String path);
       //切换当前目录到子目录
       boolean cd_to_child(String path);
       //切换到上一层目录
       boolean cd_to_parent();
    }
    
    public class GeniusException {
        //指令不存在
        static class CommondNotExistException extends Exception{
            public CommondNotExistException(String s) {
                super(s);
            }
        }
        static class CommondArgumentException extends Exception{
            public CommondArgumentException(String s) {
                super(s);
            }
        }
    }
    
    public class CommendOperation {
        //回调对象
        private ICommend listener;
        //获取输入信息
         private   Scanner mScanner;
         //保存所有指令
         private List<String> commonts;
         public CommendOperation(){
             mScanner=new Scanner(System.in);
             //将普通的Array类型转换为List
             commonts= Arrays.asList(ICmd.COMMONDS);
         }
        //接收用户输入的指令
        public void readCommend(ICommend listener) throws
                GeniusException.CommondNotExistException,CommendTool.GeniusException.CommondArgumentException {
         this.listener=listener;
            //接收指令
            String commond=mScanner.nextLine();
            // 解析指令
            parseCommend(commond);
        }
        public void parseCommend(String commond) throws
                GeniusException.CommondNotExistException,GeniusException.CommondArgumentException {
          //将指令以空格为分隔符分开
           String[] compoents=commond.split(" ");
         //获取用户指令
           String cmd= compoents[0];
           //判断指令是否存在
        if(!commonts.contains(cmd)){
            //输入指令不存在
           //抛出异常
            throw new GeniusException.CommondNotExistException("指令不存在");
        }
        //存在就解析是哪种指令
            switch (cmd){
                case ICmd.CD:
                    if(compoents.length!=2){
                   throw new GeniusException.CommondArgumentException("cd 参数不正确");
                    }
                   listener.cd_to_child(compoents[1]);
                    break;
                case ICmd.CDP:
                    if(compoents.length!=1){
                        throw new GeniusException.CommondArgumentException("cd.. 不需要参数");
                    }
                    listener.cd_to_parent();
                    break;
                case ICmd.MKDIR:
                    if(compoents.length!=2){
                        throw new GeniusException.CommondArgumentException("cd 参数不正确");
                    }
                    listener.mkdir(compoents[1]);
                    break;
                case ICmd.RM:
                    if(compoents.length!=2){
                        throw new GeniusException.CommondArgumentException("rm 参数不正确");
                    }
                 listener.remove(compoents[1]);
                    break;
                case ICmd.LS:
                    if(compoents.length!=1){
                        throw new GeniusException.CommondArgumentException("ls 不需要参数");
                    }
                    listener.list();
                case ICmd.COPY:
                    if(compoents.length!=3){
                        throw new GeniusException.CommondArgumentException("copy 参数不正确");
                    }
                    listener.copy(compoents[1],compoents[2]);
                    break;
            }
        }
    }
    
    public class FileManager {
        private static FileManager manager;
        private FileManager(){}
    
        public static FileManager getInstance(){
            if(manager==null){
                synchronized (FileManager.class){
                  if(manager==null){
                      manager =new FileManager();
                  }
                }
            }
            return manager;
        }
    
        public boolean mkdir(String path){
            File file =new File(path);
            if(file.exists()){
                return false;
            }
            //mkdir要求路径中的目录都存在
            //mkdirs 如果路径中的目录不存在,也会自动创建
            return   file.mkdir();
        }
        public boolean remove(String path){
            File file =new File(path);
            if(file.exists()){
               return file.delete();
            }
            return false;
        }
        public File[] list(String path){
            File file =new File(path);
            if(!file.exists()){
                return null;
            }
           return file.listFiles(new FilenameFilter() {
               @Override
               public boolean accept(File file, String s) {
                   if(s.startsWith(".")) {
                       return false;
                   }
                   return true;
               }
           });
        }
    
        public boolean copy(String src,String des){
         File srcfile= new File(src);
         File desfile= new File(des);
         //判断文件是否存在
            if(!srcfile.exists()||!desfile.exists()){
                return false;
            }
            //判断是不是文件
            if(srcfile.isFile()){
                //直接拷贝文件
                copyFile(src,des);
            }else {
            //创建当前目录
                desfile.mkdir();
                //需要将原目录的所有内容copy到des目录
                for (File file:srcfile.listFiles()){
                    copy(file.getAbsolutePath(),des+"\\"+file.getName());
                }
            }
            return true;
        }
        public void copyFile(String src,String des){
            try (//创建输入流
                 FileInputStream fis= new FileInputStream(src);
                 BufferedInputStream bis= new BufferedInputStream(fis);
                 //创建输出流
                 FileOutputStream fos =new FileOutputStream(des);
                 BufferedOutputStream bos= new BufferedOutputStream(fos);){
                //创建Buffer
                byte[] Buffer=new byte[1024];
                int len=0;
                while ((len=bis.read())!=-1){
                    bos.write(Buffer);
                }
                bos.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    public class CommendTool implements ICommend{
        //默认启动起来显示操作的目录
        private static final String DESK_TOP="C:\\Users\\86178\\Desktop";
        //记录当前操作的目录路径
        private StringBuilder currentDirPath;
    
        public CommendTool() {
           currentDirPath=new StringBuilder(DESK_TOP);
        }
        //启动命令行工具
      public void start() {
          //创建读取指令的对象
          CommendOperation operation = new CommendOperation();
          System.out.println("欢迎使用计算机鬼才定制版命令行工具");
          while (true) {
              //显示目录替换
              showParent();
              //用户输入指令
              try {
                  operation.readCommend(this);
              } catch (Exception e) {
                  System.out.println(e.getMessage());
              }
          }
      }
    
      private void showParent(){
            //获取最后一个\\的index
          int start= currentDirPath.lastIndexOf("\\");
          //获取最后的内容
        String parent=  currentDirPath.substring(start);
        //输出提示内容
          System.out.print(parent+"#");
      }
    
        @Override
        public boolean list() {
           File[] files= FileManager.getInstance().list(currentDirPath.toString());
           for(File file:files){
               //获取文件名
               String name= file.getName();
               //获取文件长度
               long size= file.length();
               long kb= size/1024;
               long by=size%1024;
               System.out.print(name);
               for(int i=0;i<50-name.length();i++){
                   System.out.print(" ");
               }
               System.out.println(name+"      "+kb+"."+by+"kb");
           }
            return false;
        }
    
        @Override
        public boolean mkdir(String path) {
            //拼接完整路径
            String dirPath=currentDirPath.toString()+"\\"+path;
           FileManager.getInstance().mkdir(dirPath);
            return false;
        }
    
        @Override
        public boolean copy(String src, String des) {
            String srcPath=currentDirPath.toString()+"\\"+src;
            String desPath=currentDirPath.toString()+"\\"+des;
            FileManager.getInstance().copy(srcPath,desPath);
            return false;
        }
    
        @Override
        public boolean remove(String path) {
            String dirPath=currentDirPath.toString()+"\\"+path;
             FileManager.getInstance().remove(dirPath);
            return false;
        }
    
        @Override
        public boolean cd_to_child(String path) {
           currentDirPath= currentDirPath.append("\\"+path);
            return false;
        }
    
        @Override
        public boolean cd_to_parent() {
          int start= currentDirPath.toString().lastIndexOf("\\");
          int end= currentDirPath.length();
          currentDirPath.delete(start,end);
            return false;
        }
    }
    
    public class MyClass {
        public static void main(String[] args) {
        CommendTool tool=new CommendTool();
        tool.start();
        }
    }
    

    相关文章

      网友评论

          本文标题:Java实战开发(4)——命令行工具(类似cmd指令)

          本文链接:https://www.haomeiwen.com/subject/wwkubhtx.html