用java模拟windows下的dir指令

胡扯

  这是一个很久以前就写了的代码,一直懒于相关的文档说明,所以一直一拖再拖,拖了很久。这个版本的说明还是比较简单的,如果整个张开来说的话,其实可以说上不少内容的。具体的源码可以到我的个人github上下载:https://github.com/DoneSpeak/java_Dos_Dir

功能实现列表

这里写图片描述

需求分析

指令功能了解

  通过help dir指令,我们可以查看dir指令的相关参数很选项以及对应的功能。当然为了更好的理解指令的的使用,我们还需要到网上去搜一些相关的讲解资料以及自己动手实际操作一遍。

dir基本功能目录

指令分类

  为了更好的吃掉这一只大象,我们需要将其划分成更容易实现和理解的多个小部分。通过实际了解以及实际操作,我们可以将所有的选项划分如下:

类型指令作用指令间关系
筛选/A[ D、R、H、A、S、I、L 、-D、-R、-H、-A、-S、-I、-L ]对指定目录中的目录及文件进行筛选并列(可叠加)相反者以出现为准
排序/O[N、S、E、D、G、-N、-S、-E、-D、-G]对指定目录中的目录及文件进行排序并列(可叠加)相反者以出现为准
修饰/T[W、C、A] /L /Q /A[I、L] /C对显示内容的格式进行修改大类并列(可叠加)T中属性:互斥(优先级从左到右)
显示/B、/X、 /D、/W、/N、/-N决定显示的方式互斥(优先级从左到右,最低一级为默认显示,显示方式与/N相同)
其他/P、/S显示时的辅助功能并列

  此外,以上不同类型选项之间均为并列关系。

指令组合功能

  通过个人的实际使用,我将需要实现的功能又总结如下:

特点描述
多指令操作D:\TestFolder /a-h-r-s /s /d/w/x/n/oe-ns /l/t:wca/q
多种输入方式,且设置默认目录
  1. 支持大小写 Java Dir /A:h /oS
  2. 无限定输入顺序 Java Dir /a /a:-h D:\TestFolder/o /n
  3. 多种分隔符 Java Dir /a,/a:-h;D:\TestFolder/o;/n
  4. 默认目录为user.home
多路径操作Java Dir D:\ D:\TestFolder D:\TestFolder\tttxt .p2
多重排序
  1. Java Dir D:\TestFolder /oesn
  2. Java Dir D:\TestFolder /osen
  3. Java Dir D:\TestFolder /ose-n
文件检索
  1. 准确检索 Java Dir D:\TestFolder\tdd.txt /s
  2. 正则表达式检索 Java Dir “D:\TestFolder\*.txt”
异常处理当用户输入一些错误的指令是,会输出异常信息。
显示帮助文件当输入的内容中含有 /? 或者 /– 同时这两个符号之前的命令均没有问题时,会输出帮助信息。
Java Dir /ahr/–/os D:\TestFolder
Java Dir /?

代码结构

整体结构

  指令输入主要包含三种信息:dir指令(包含选项)、检索目录或者文件。由于指令输入可以很长,比如:D:\TestFolder /a-h-r-s /s /d/w/x/n/oe-ns /l/t:wca/q,该指令中包含了很多的组合信息,所以第一步我们需要对指令进行解析,解析出指令及相关选项,目录或者文件,以及判断出目录或者文件的字符串是否为正则表达式。解析完成之后,按照分类以及选项之间的相互关系对指令进行筛选,排序和显示(显示中会包含修饰)。

整体框架

详细结结构

  代码需要运行在windows下的cmd界面中,所以输入的字符串为参数数组args[]。之后的流程是对以上整体结果进行细化。

详细结构

  在处理完成之后,我们在显示之前进行了一次修饰,由于需要考虑显示类型选项之间的互斥关系,以及一些其他的只对部分显示选项才有效,所显示过程还有如下的流程关系。
comdE.modifyElements对显示进行修饰

几个重要的类

类名成员变量描述
CommandElementsArrayList actionA、ArrayList actionO、ArrayList commandPS、ArrayList modifyElements、char showType保存命令及其命令选项
SearchObjectString fileName、String filePath、boolean isRegex保存路径及文件名,并记录是否为正则表达式
FileInfoString date、String size、String type、String longname、String shortname、String owner保存需要输出的数据对象,用于显示时的存储格式化后的字符串

具体实现

指令解析

CommandTokenizer. commandTokenizer
(String[] source, CommandElements cmdE, ArrayList<SearchObject> searchObj)

  指令解析主要由类CommandTokenizer的commandTokenizer方法实现。source为输入的指令字符串,参数cmdE和searchObj为解析之后结果。

指令解析

指令解析

选项a

重点:获取文件的属性
为了获取到文件的属性,我们会使用到java.io.File,但是我们知道,该类能够获取到的文件属性非常的少,因而这里我们需要使用到java.nio.file.*类。相关部分api 可以看java官网的文档 http://docs.oracle.com/javase/8/docs/api/ 或者自己查找相关资料。

选项o

重点:排序、多重排序

基本代码结构

如下仅展示部分代码,其他的排序处理方法也如此。

//Actions.actionO
//如下代码中FileComparator_DirFFileL是一个继承了接口Comparator的类,该类规定文件及目录按照目录在文件前面的方式排序
ArrayList<File> fileList = fileListList.get(0);
FileComparator_DirFFileL fc = new FileComparator_DirFFileL();
fileList.sort(fc);

···

class FileComparator_DirFFileL implements Comparator<File> {
    @Override
    public int compare(File f1, File f2) {
        if (f1.isDirectory() && f2.isDirectory())
            return f1.compareTo(f2);
        else if (f1.isDirectory() && !f2.isDirectory())
            return -1;
        else if (!f1.isDirectory() && f2.isDirectory())
            return 1;
        else
            return f1.compareTo(f2);
    }
}

多重排序的原理

  不难注意到,我们这里有两个特别的数据结构ArrayList<ArrayList<File>>ArrayList<File>,对应下图就是ArrayList<ArrayList<File>>为绿色部分,而ArrayList<File>对应蓝色部分。
  我们以指令Java Dir D:\TestFolder /oes为例:开始的时候,所有的文件没有进行任何规则的排序处理,所以我们可以理解为其所有的文件都是一样的,之后进行按照拓展名进行升序排序,这时候中间部分的每个蓝色的矩形,也就是一个的ArrayList<File>。每个这样的矩形都是具有相同的拓展名。之后在按照大小进行升序排序,也就得到了更加密的小矩形,这时的每个小矩形都是具有相同的文件拓展名和相同的大小。通过这种巧妙的方式,我们就便可以在保障数据结构(保障相同的数据结构可以保障能够使用相类似的数据处理过程)的前提下实现多重排序。

多重排序原理

多重排序原理

排序操作的O选项代码如下,其中省略部分为结果相类似的其他排序操作。

public static ArrayList<File> actionO(CommandElements cmdE, ArrayList<ArrayList<File>> fileListList)
        throws IOException {
     isEmpty(没有) size=1(第一个为1,仅有/a) size>1(含有其他属性) n(名字) s(大小) e(扩展名)
     d(时间) g(组目录优先) 负数表示-
    if (cmdE.actionO.isEmpty()) { // 不排序
        return fileListList.get(0);
    }
    if (cmdE.actionO.size() == 1) { // 只排 /0
        ArrayList<File> fileList = fileListList.get(0);
        FileComparator_DirFFileL fc = new FileComparator_DirFFileL();
        fileList.sort(fc);
        return fileList;
    }
    Iterator<Integer> iter = cmdE.actionO.iterator();
    iter.next();// 删除第一个表示只有/o的元素
    while (iter.hasNext()) {
        Comparator<File> fc;
        int action = iter.next();
        switch (action) {
        case (int) 'n': 
        case -(int) 'n':{
            if(action == (int)'n')
                fc = new FileComparator_Name();
            else
                fc = new FileComparator_ReName();

            sortWithDifferentFC(fileListList, fc);
            ArrayList<ArrayList<File>> finalfll = new ArrayList<ArrayList<File>>();
            Iterator<ArrayList<File>> aFIter = fileListList.iterator();
            while (aFIter.hasNext()) {
                ArrayList<File> sameFile = new ArrayList<File>();
                ArrayList<File> flist = aFIter.next();
                Iterator<File> afi = flist.iterator();
                File f1 = null;
                if(afi.hasNext()){
                    f1 = afi.next();
                    sameFile.add(f1);
                }

                while(afi.hasNext()){   //同名称的放到同一个数组中
                    ArrayList<File> blank = new ArrayList<File>();
                    File f2 = afi.next();
                    if(f1.getName().equals(f2.getName())){
                        sameFile.add(f2);
                        f1 = f2;
                    }           
                    else{
                        f1 = f2;
                        finalfll.add(sameFile);
                        sameFile=blank;
                        sameFile.add(f1);
                    }
                }
                finalfll.add(sameFile);
            }
            fileListList = finalfll;
            break;
        }

        ···

    return fileList;
}

排序规则结果继承类。

class FileComparator_ReName implements Comparator<File> {
    @Override
    public int compare(File f1, File f2) {
        if(f1.getName().equals(f2.getName()))
            return 1;
        return f2.compareTo(f1);
    }
}

排序函数

public static void sortWithDifferentFC(ArrayList<ArrayList<File>> fileListList, Comparator<File> fc) {
    ArrayList<ArrayList<File>> tfll = new ArrayList<ArrayList<File>>();
    Iterator<ArrayList<File>> aFIter = fileListList.iterator();
    while (aFIter.hasNext()) {
        ArrayList<File> flist = aFIter.next();
        flist.sort(fc);
        tfll.add(flist);
    }
    fileListList = tfll;
}

修饰类 -/T[W、C、A] /L /Q /A[I、L] /C

重点:字符长的格式化
显示修饰中,部分选项需要获取一些属性或者对字符串进行格式化。

  • 获取文件上次写入时间 /tw
  • 获取文件创建时间 /tc
  • 获取文件上次访问时间 /ta
  • 获取文件所有者 /q
  • 获取所有者
  • 判断文件类型
  • 数值格式化
  • 消除拓展名

不同显示方式

异常处理

重点:重载Exception类

异常显示

//使用enum使得代码更加简洁
enum ExceptionEnum {
    WrongParameter, 
    WrongSwitch, 
    ProgrammeMistack, 
    PrintHelpInfo , 
    GrammaticalMistake, 
    IsCannotIdentifyDevice, 
    FileCannotFind
}

@SuppressWarnings("serial")
public class CommandException extends Exception{

    String message;

    public CommandException(ExceptionEnum exEnum,String parameter){
        if(exEnum.equals(ExceptionEnum.WrongSwitch)){
            message = "无效开关 - \""+parameter+"\"。";
        }
        else if(exEnum.equals(ExceptionEnum.WrongParameter)){
            message = "参数格式不正确 - \""+parameter+"\"。";
        }
        else if(exEnum.equals(ExceptionEnum.ProgrammeMistack)){
            message = "开发者编程出错!";
        }
        else if(exEnum.equals(ExceptionEnum.PrintHelpInfo)){
            message = "···"; //这里省略了帮助的信息
        }
        else if(exEnum.equals(ExceptionEnum.GrammaticalMistake)){
            message = "文件名、目录名或卷标语法不正确。";
        }
        else if(exEnum.equals(ExceptionEnum.IsCannotIdentifyDevice)){
            message = "\""+parameter + "\" 是无法识别设备。";
        }
        else if(exEnum.equals(ExceptionEnum.FileCannotFind)){
            message = "找不到文件";
        }
    }
    @Override
    public String getMessage(){
        return message;
    }
}

TestCommandTokenizer

  由于我的程序主要依赖于CommandTokenizer处理的来的信息(包括dir指令,检索路径和检索文件名),因此CommandTokenizer的正确运行是我后续运行的保障,所以我专门写了这么一个含有主函数的类来检查我输入的内容经CommandTonizer处理后得到的信息是否正确。
  举例如下:
TestCommandTokenizer

searchObjext中的数据是:
1. 检索文件名(是正则表达式的已转为正则表达式),文件路径,文件名是否为正则表达式
2. actionA和actionO中是他们的属性,笑脸(在eclipse中是小方格)表示含有/A或 /O 这个指令。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值