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

2020-04-20  本文已影响0人  让时间走12138

本节内容

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();
    }
}
上一篇 下一篇

猜你喜欢

热点阅读