前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >杂篇-从整理文件发起的杂谈[-File-]

杂篇-从整理文件发起的杂谈[-File-]

作者头像
张风捷特烈
发布2019-03-04 15:02:15
4530
发布2019-03-04 15:02:15
举报

有些东西很简单,简单到你不想去想,比如:为什么天是蓝的?--局限物语

零、前言

说一下本篇的初衷: coder盘作为工作盘有点乱,想整理一下 也想寻求一个方便管理工程的点子,既然File类玩的滚瓜烂熟,何妨玩一下 加之File的天然递归性,再熟悉一下递归也不错,所以便有此篇, 本文前奏有点长,好戏在后面----注意:本文的重点不是操作,而是思想

coder.png


一、创建Filer类

java的File对象只是提供一些基本信息,这也是情理之中 感觉可以封装一下,提供更多的信息,比如下面的,文件夹大小,子文件个数等

信息.png


1.先举一个简单的例子:目录结构如下:
代码语言:javascript
复制
|---edite
    |---top
    |---toly
        |---界面编程GUI
            |---Edit.java
            |---FileHelper.java
            |---IOUtils.java
    |---edit.jar
    |---main.txt

2.Filer信息封装

先实现子文件夹、文件的个数、文件夹大小三个属性

代码语言:javascript
复制
public class Filer {
    private String name;//文件夹名
    private int dirCount;//子文件夹数量---不包括自身
    private int fileCount;//文件的个数
    private long length; //文件夹大小
    
    public File getFile() {
        return file;
    }
    public int getDirCount() {
        return dirCount - 1;
    }
    public int getFileCount() {
        return fileCount;
    }
    public long getLength() {
        return length;
    }
}

3.定义文件节点类

考虑到一个文件夹下或有多个文件,这里用ArrayList装一下, 考虑到默认情况下ArrayList的初始数组为10个,这里取4个(很可能一个文件夹里就一两个文件)

代码语言:javascript
复制
/**
 * 文件节点
 */
private class FileNode {
    public ArrayList<FileNode> child;//子节点集合
    public File file; //文件路径
    public FileNode(File file) {
        this.file = file;
        child = new ArrayList<>(4);
    }
}

4.Filer的初始化
代码语言:javascript
复制
 private FileNode root;//根节点
 public Filer(String rootPath) {
     file = new File(rootPath);
     root = new FileNode(file);//初始化根节点
 }

5.文件的扫描

你可以结合下面的分析,自己debug走一走

代码语言:javascript
复制
public Filer(String rootPath) {
    file = new File(rootPath);
    root = new FileNode(file);
    scan(root);//扫描根目录
}

private void scan(FileNode node) {
    File file = node.file;
    if (file.isFile()) {//如果节点是文件
        return;
    }
    File[] files = file.listFiles();
    for (File f : files) {
        FileNode child = new FileNode(f);
        node.child.add(child);
        if (f.isDirectory()) {
            scan(child);
        }
    }
}

第一次调用scan时,如果是文件夹,就遍历文件夹, 里面不管是文件还是文件夹都加到child里

当遇到文件夹是便会触发scan来扫描该文件夹,这便是最简单的递归 无返回值,只是触发行为(实际每次触发scan方法调用完成,会有方法出栈的步骤)

目录结构.png

就这样一个树形的结构就形成了

debug查看一下.png


6.既然一个一个节点连接了这颗树

那么完全可以在扫描的时候多做一些事,比如维护那三个成员变量

代码语言:javascript
复制
private void scan(FileNode node) {
    File file = node.file;
    if (file.isFile()) {//如果节点是文件
        return;
    }
    File[] files = file.listFiles();
    for (File f : files) {
        FileNode child = new FileNode(f);
        node.child.add(child);
        if (f.isDirectory()) {
            dirCount++;//每调用一次说明有一个文件夹
            scan(child);
        } else {
            fileCount++;//每调用一次说明有一个文件
            length += f.length();//维护length
        }
    }
}

7.调用

打印结果.png

代码语言:javascript
复制
public class MakeDirInfo {
    public static void main(String[] args) {
        File root = new File("J:\\edite");
        Filer filer = new Filer(root.getAbsolutePath());
        
        long length = filer.getLength();//17.86621KB
        System.out.println(Formater.format_B_KB_MB_GB(length));//5
        long modifyTime = filer.getFile().lastModified();//2018-10-06 15:19:39
        System.out.println(Formater.format_yyyy_MM_dd_kk_mm_ss(modifyTime));//5
        System.out.println(filer.getFile().getName());//edite
        System.out.println(+filer.getDirCount()+"个文件夹");//3
        System.out.println(filer.getFileCount()+"个文夹");//5
        System.out.println(filer.getFileFilter().get(0).count);//5
    }
}

用一个大一些的文件夹看一下:感觉7秒多挺慢的,电脑自身的查看器反应完要9秒,所以还好吧

打印结果2.png


二、全副武装
1.统计文件夹中文件类型

想一下,花了7秒得到三个属性,而且把文件都遍历一边了,感觉有点亏 现在想看一下有多少种文件,这要求不过分吧,想想也简单,看一下后缀名就行了

打印.png

代码语言:javascript
复制
public Filer(String rootPath) {
    mSet = new HashSet<>();
    ...
}

private void scan(FileNode node) {
    File file = node.file;
    if (file.isFile()) {//如果节点是文件
        return;
    }
    File[] files = file.listFiles();
    for (File f : files) {
        FileNode child = new FileNode(f);
        node.child.add(child);
        if (f.isDirectory()) {
            dirCount++;//每调用一次说明有一个文件夹
            scan(child);
        } else {
            fileCount++;//每调用一次说明有一个文件
            String fileName = f.getName();
            String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
            mSet.add(suffix);
            length += f.length();
        }
    }
}

2.策略的出现

这有个问题,也许最近设计模式想多了,六大原则时刻在心 Filer在已经很好的完成了它的扫描工作,这里让Filer多一个成员变量mSet 感觉不爽,else里的三句代码看着也不优雅,如果需要改动,还有找在哪里, 代码如果多起来,茫茫码海,哪去找这三行!何不提取策略呢?

抽取策略.png

代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2019/2/13/013:13:44
 * 邮箱:1981462002@qq.com
 * 说明:后缀名过滤器
 */
public class SuffixFilter {
    private Set<String> suffixs;

    public Set<String> getSuffixs() {
        return suffixs;
    }

    public SuffixFilter() {
        suffixs = new HashSet<>();
    }

    public void filter(File file) {
        String fileName = file.getName();
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        suffixs.add(suffix);
    }
}

---->[Filer]-------------
private SuffixFilter mFilter;
public Filer(SuffixFilter filter) {
    mFilter = filter;
}
public void setFilter(SuffixFilter filter) {
    mFilter = filter;
}

public void scan() {
    scan(root);
}

public void scan() {
...
else {
    fileCount++;//每调用一次说明有一个文件
    if (mFilter != null) {
        mFilter.filter(f);
    }
    length += f.length();
}

---->[使用]-------------
Filer filer = new Filer("J:\\edite");
SuffixFilter filter = new SuffixFilter();
filer.setFilter(filter);//设置过滤器
filer.scan();
Set<String> suffixs = filter.getSuffixs();
for (String suffix : suffixs) {
    System.out.print(suffix+"、");
}

结果ok.png


3.当需要修改时,分离的优势显现

这样就是得Filer类和获取文件类型这个动作解耦,Filter只需要关注扫描任务 比如有些文件名没有后缀名,这样时就要修改策略,总不能都算一种文件吧? 有专门负责的类,只需去修改SuffixFilter的过滤方法就行了,比在Filer里好些

代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2019/2/13/013:13:44
 * 邮箱:1981462002@qq.com
 * 说明:后缀名过滤器
 */
public class SuffixFilter {
    private Set<String> suffixs;
    public Set<String> getSuffixs() {
        return suffixs;
    }
    public SuffixFilter() {
        suffixs = new HashSet<>();
    }
    public void filter(File file) {
        String fileName = file.getName();
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        if (suffix.length() < 10) {//如果长度大于10,是other
            suffixs.add(suffix);
        }else {
            suffixs.add("other");
        }
    }
}

所有格式.png


4.抽象与拓展

既然是策略,就可以有多种,所以抽象出共性,自定义个性来拓展 很明显,有一个公共的抽象方法,filter(File),既然是过滤,应该有过滤条件

面向接口.png

代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2019/2/13/013:14:31
 * 邮箱:1981462002@qq.com
 * 说明:文件过滤接口
 */
public interface FileFilter {
    /**
     * 根据路径判断是否过滤出
     * @param file 文件
     * @return 是否可以执行filter
     */
    boolean iCanGo(File file);

    /**
     * 过滤的逻辑操作
     * @param file 文件
     */
    void filter(File file);
}

/**
 * 作者:张风捷特烈
 * 时间:2019/2/13/013:13:44
 * 邮箱:1981462002@qq.com
 * 说明:后缀名过滤器
 */
public class SuffixFilter implements FileFilter{
    private Set<String> suffixs;
    public Set<String> getSuffixs() {
        return suffixs;
    }
    public SuffixFilter() {
        suffixs = new HashSet<>();
    }
    @Override
    public boolean iCanGo(File file) {
        return file.isFile();
    }
    public void filter(File file) {
        String fileName = file.getName();
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        if (suffix.length() < 10) {//如果长度大于10,是other
            suffixs.add(suffix);
        }else {
            suffixs.add("other");
        }
    }
}


---->[Filer]----------
private FileFilter mFilter;
public Filer(FileFilter filter) {
    mFilter = filter;
}
public void setFilter(FileFilter filter) {
    mFilter = filter;
}

public void scan() {
    ...
    for (File f : files) {
    FileNode child = new FileNode(f);
    child.deep = curDeep;
    node.child.add(child);
    if (mFilter != null && mFilter.iCanGo(f)) {
        mFilter.filter(f);
    }
    ...    
}

5.基于接口的功能拓展

好好的为什么要加个接口,是我初学java时的一个大疑问,导致我设计模式稀里糊涂 现在要添加一个获取一个文件夹下的所有java文件的功能,有了接口就非常方便了

抽象与实现的拓展.png

代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2019/2/13/013:10:59
 * 邮箱:1981462002@qq.com
 * 说明:获取一个文件夹下所有java文件路径
 */
public class JavaFilter implements FileFilter {
    private ArrayList<String> javaFiles;
    public ArrayList<String> getJavaFiles() {
        return javaFiles;
    }
    public JavaFilter() {
        javaFiles = new ArrayList<>();
    }
    @Override
    public boolean iCanGo(File file) {
        String path = file.getAbsolutePath();
        String suffix = path.substring(path.lastIndexOf(".") + 1);
        return suffix.equals("java");
    }
    @Override
    public void filter(File file) {
        javaFiles.add(file.getAbsolutePath());
    }
}

---->[使用]----------
Filer filer = new Filer("J:\\Github");
JavaFilter javaFilter = new JavaFilter();
filer.setFilter(javaFilter);
filer.scan();
for (String s : javaFilter.getJavaFiles()) {
    System.out.println(s);
}

java滤出.png

可见过滤操作已经和Filer分离了,拓展了一个查看所有java文件的功能 没有修改Filer里的任何代码,对于Filer来说就是优秀的 iCanGo方法用来控制筛选,filter用来操作...一个控制系法师,一个输出系战士,吊打无误

当然你也可以将类型变成参数,通过构造来,这样会更方便

代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2019/2/13/013:10:59
 * 邮箱:1981462002@qq.com
 * 说明:获取一个文件夹下所有某类型文件的路径
 */
public class TypeFilter implements FileFilter {
    private ArrayList<String> javaFiles;
    private String type;
    public TypeFilter(String type) {
        this.type = type;
        javaFiles = new ArrayList<>();
    }
    public ArrayList<String> getJavaFiles() {
        return javaFiles;
    }
    @Override
    public boolean iCanGo(File file) {
        String path = file.getAbsolutePath();
        String suffix = path.substring(path.lastIndexOf(".") + 1);
        return suffix.equals(type);
    }
    @Override
    public void filter(File file) {
        javaFiles.add(file.getAbsolutePath());
    }
}

6.批量修改操作

有很多时候感觉很像接力赛,一旦将棒子交给下一个人,你就不用跑了 Filer就是这样,我把遍历到的File交给你Filter,你爱怎搞自己搞,跟我无关 有点像服务器发数据,数据给你了我就没事了,你怎么处理你自己看着办 现在我想要将一个文件夹下的所有java文件结尾加一行字的需求: 怎么操作我说了算,甚至不用新建类,写个匿名内部类就行了,拿到文件,就写呗!

代码语言:javascript
复制
Filer filer = new Filer("J:\\edite");
filer.setFilter(new FileFilter() {
    @Override
    public boolean iCanGo(String path) {
        String path = file.getAbsolutePath();
        String suffix = path.substring(path.lastIndexOf(".") + 1);
        return suffix.equals("java");
    }
    @Override
    public void filter(File file) {
        try {
            FileWriter fw = new FileWriter(file, true);
            String time = Formater.format_yyyy_MM_dd_kk_mm_ss(System.currentTimeMillis());
            fw.write("// 张风捷特烈 修改:" + time);
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
});
filer.scan();

这样来看,批量改名字还不是手到擒来


7.多个过滤器

反正遍历都遍历了,不用白不用,多几个过滤器,多几重操作

代码语言:javascript
复制
//过滤器集合
private ArrayList<FileFilter> mFilters = new ArrayList<>();
public void addFilter(FileFilter countFilter) {
    mFilters.add(countFilter);
}

public void scan() {
    ...
    for (FileFilter filter : mFilters) {
        if (filter != null && filter.iCanGo(f)) {
            filter.filter(f);
        }
    }
    ...
    
---->[使用]-----
Filer filer = new Filer("J:\\edite");
JavaEditer javaEditer = new JavaEditer();
TypeFilter typeFilter = new TypeFilter("java");
filer.addFilter(javaEditer);
filer.addFilter(typeFilter);
filer.scan();
for (String s : typeFilter.getFiles()) {
    System.out.println(s);
}

三、言归正传
1.添加节点深度字段并维护

每当文件夹时curDeep++,跳出一次scan方法时curDeep--

打印节点深度.png

代码语言:javascript
复制
public class Filer {
    ...
    int curDeep;//节点深度

---->[Filer#scan]---------------
private void scan(FileNode node) {
    File file = node.file;
    if (file.isFile()) {//如果节点是文件
        return;
    }
    File[] files = file.listFiles();
    for (File f : files) {
        FileNode child = new FileNode(f);
        child.deep = curDeep;
        node.child.add(child);
        System.out.println(child.file.getAbsolutePath() + "--" + child.deep);
        if (f.isDirectory()) {
            dirCount++;//每调用一次说明有一个文件夹
            curDeep++;
            scan(child);
        } else {
            fileCount++;//每调用一次说明有一个文件
            for (FileFilter filter : mFilters) {
                if (filter != null && filter.iCanGo(f.getAbsolutePath())) {
                    filter.filter(f);
                }
            }
            length += f.length();
        }
    }
    curDeep--;
}

/**
 * 文件节点
 */
private class FileNode {
    ...
    public int deep;//深度

}

2.修改接口

现在想把deep这个参数传出去...有两个方法,一种:修改接口!!! 第二种:新建接口,还好实现类不是很多,这里改一下接口吧...

代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2019/2/13/013:14:31
 * 邮箱:1981462002@qq.com
 * 说明:文件过滤接口
 */
public interface FileFilter {
    /**
     * 根据路径判断是否过滤出
     * @param path 路径
     * @return 是否可以执行filter
     */
    boolean iCanGo(File path);
    /**
     * 过滤的逻辑操作
     * @param file 文件
     * @param deep 该文件深度
     */
    void filter(File file, int deep);
}

---->[Filer#scan]---------------------------
private void scan(FileNode node) {
    ...
    for (File f : files) {
        FileNode child = new FileNode(f);
        child.deep = curDeep;
        node.child.add(child);
        for (FileFilter filter : mFilters) {
            if (filter != null && filter.iCanGo(f)) {
                filter.filter(child.file, child.deep);//回调出去深度
            }
        }
    ...
}

3.打印目录结构

一直觉得目录结构挺帅气,今天总算自己弄出来了,可喜可贺,可喜可贺

打印目录结构.png

代码语言:javascript
复制
-----------------创建目录结构过滤器----------------
|--这里iCanGo返回true,也就是畅通无阻,相当于一个监听器
/**
 * 作者:张风捷特烈
 * 时间:2019/2/13/013:10:59
 * 邮箱:1981462002@qq.com
 * 说明:创建目录结构
 */
public class StructureBuilder implements FileFilter {

    String prefix;
    String blank;
    StringBuilder sb = new StringBuilder("目录结构:\n");

    public String getStructure() {
        return sb.toString();
    }

    public StructureBuilder() {
        this("|---", "    ");
    }

    public StructureBuilder(String prefix, String blank) {
        this.prefix = prefix;
        this.blank = blank;
    }

    @Override
    public boolean iCanGo(File file) {
        return true;
    }

    @Override
    public void filter(File file, int deep) {
        sb
                .append(blankBuilder(blank, deep))
                .append(prefix)
                .append(file.getName())
                .append("\n");

    }

    private static StringBuilder blankBuilder(String symbol, int num) {
        StringBuilder stringBuffer = new StringBuilder();
        for (int i = 0; i < num; i++) {
            stringBuffer.append(symbol);
        }
        return stringBuffer;
    }
}

---->[使用]---------
Filer filer = new Filer("J:\\C++");
StructureBuilder structureBuilder = new StructureBuilder();
filer.addFilter(structureBuilder);
filer.scan();
System.out.println(structureBuilder.getStructure());

这样,样式随便你来定义:new StructureBuilder("|---","····")

自定义样式.png


4.保存文件目录树

在根目录下保存一个文件目录树,这样方便查看,也让你知道大概这个文件夹怎么样 有多少文件,字符串在这里,你可以随意分析,玩弄,是不是很有趣

保存文件目录树.png

代码语言:javascript
复制
---->[StructureBuilder#writeFile]---------------------------
/**
 * 将文件结构写入文件
 * @param file 文件
 */
public void writeFile(File file) {
    FileWriter fw = null;
    try {
        fw = new FileWriter(file);
        String time = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss", Locale.CHINA)
                .format(System.currentTimeMillis());
        fw.write("张风捷特烈--修改于" + time + "\n");
        fw.write(sb.toString());
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (fw != null) {
                fw.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

看这个目录里有11W个文件,好吧,吓我一跳,生成也只用了7秒多

文件.png


5.创建json格式的文件描述文件夹

没想到用个节点之后这么方便,Gosn秒出结构 依赖:implementation 'com.google.code.gson:gson:2.8.5' 好了,Json在手,去解析着玩吧

代码语言:javascript
复制
Filer filer = new Filer("J:\\edite");
filer.scan();
String json = new GsonBuilder().setPrettyPrinting().create().toJson(filer);
System.out.println(json);
代码语言:javascript
复制
{
  "file": {
    "path": "J:\\edite"
  },
  "dirCount": 4,
  "fileCount": 6,
  "length": 11890,
  "curDeep": -1,
  "root": {
    "child": [
      {
        "child": [],
        "file": {
          "path": "J:\\edite\\edit.jar"
        },
        "deep": 0
      },
      {
        "child": [],
        "file": {
          "path": "J:\\edite\\main.txt"
        },
        "deep": 0
      },
      {
        "child": [],
        "file": {
          "path": "J:\\edite\\structure.txt"
        },
        "deep": 0
      },
      {
        "child": [
          {
            "child": [
              {
                "child": [
                  {
                    "child": [],
                    "file": {
                      "path": "J:\\edite\\top\\toly\\界面编程GUI\\Edit.java"
                    },
                    "deep": 3
                  },
                  {
                    "child": [],
                    "file": {
                      "path": "J:\\edite\\top\\toly\\界面编程GUI\\FileHelper.java"
                    },
                    "deep": 3
                  },
                  {
                    "child": [],
                    "file": {
                      "path": "J:\\edite\\top\\toly\\界面编程GUI\\IOUtils.java"
                    },
                    "deep": 3
                  }
                ],
                "file": {
                  "path": "J:\\edite\\top\\toly\\界面编程GUI"
                },
                "deep": 2
              }
            ],
            "file": {
              "path": "J:\\edite\\top\\toly"
            },
            "deep": 1
          }
        ],
        "file": {
          "path": "J:\\edite\\top"
        },
        "deep": 0
      }
    ],
    "file": {
      "path": "J:\\edite"
    },
    "deep": 0
  }
}

6.还是自己优化一下json吧

上面的json看着不爽,把root字段屏蔽掉,看一下本项目目录信息吧 保存到文件夹里也是一样的,这里就不演示了

代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2019/2/13/013:10:59
 * 邮箱:1981462002@qq.com
 * 说明:创建目录JSON结构
 */
public class JsonDirBuilder implements FileFilter {
    List<Filer> dirs;
    List<Filer> files;
    public String getStructure() {
        return new GsonBuilder().setPrettyPrinting().create().toJson(dirs);
    }
    public JsonDirBuilder() {
        dirs = new ArrayList<>();
        files = new ArrayList<>();
    }
    @Override
    public boolean iCanGo(File file) {
        return true;
    }
    @Override
    public void filter(File file, int deep) {
        Filer filer = new Filer(file.getAbsolutePath());
        filer.scan();
        filer.curDeep = deep;
        if (file.isDirectory()) {
            dirs.add(filer);
        } else {
            files.add(filer);
        }
    }
    /**
     * 将文件结构写入文件
     *
     * @param file 文件
     */
    public void writeFile(File file) {
        FileWriter fw = null;
        try {
            fw = new FileWriter(file);
            String time = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss", Locale.CHINA)
                    .format(System.currentTimeMillis());
            fw.write("张风捷特烈--修改于" + time + "\n");
            fw.write(getStructure());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
代码语言:javascript
复制
[
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\out"
    },
    "dirCount": 7,
    "fileCount": 12,
    "length": 22907,
    "curDeep": 0
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\out\\production"
    },
    "dirCount": 6,
    "fileCount": 12,
    "length": 22907,
    "curDeep": 1
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\out\\production\\classes"
    },
    "dirCount": 5,
    "fileCount": 12,
    "length": 22907,
    "curDeep": 2
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\out\\production\\classes\\app"
    },
    "dirCount": 1,
    "fileCount": 4,
    "length": 9499,
    "curDeep": 3
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\out\\production\\classes\\bean"
    },
    "dirCount": 1,
    "fileCount": 1,
    "length": 2259,
    "curDeep": 3
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\out\\production\\classes\\filter"
    },
    "dirCount": 1,
    "fileCount": 6,
    "length": 9800,
    "curDeep": 3
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\out\\production\\classes\\utils"
    },
    "dirCount": 1,
    "fileCount": 1,
    "length": 1349,
    "curDeep": 3
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\src"
    },
    "dirCount": 11,
    "fileCount": 11,
    "length": 18285,
    "curDeep": 0
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\src\\main"
    },
    "dirCount": 7,
    "fileCount": 11,
    "length": 18285,
    "curDeep": 1
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\src\\main\\java"
    },
    "dirCount": 5,
    "fileCount": 11,
    "length": 18285,
    "curDeep": 2
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\src\\main\\java\\app"
    },
    "dirCount": 1,
    "fileCount": 3,
    "length": 7919,
    "curDeep": 3
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\src\\main\\java\\bean"
    },
    "dirCount": 1,
    "fileCount": 1,
    "length": 1935,
    "curDeep": 3
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\src\\main\\java\\filter"
    },
    "dirCount": 1,
    "fileCount": 6,
    "length": 7477,
    "curDeep": 3
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\src\\main\\java\\utils"
    },
    "dirCount": 1,
    "fileCount": 1,
    "length": 954,
    "curDeep": 3
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\src\\main\\resources"
    },
    "dirCount": 1,
    "fileCount": 0,
    "length": 0,
    "curDeep": 2
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\src\\test"
    },
    "dirCount": 3,
    "fileCount": 0,
    "length": 0,
    "curDeep": 1
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\src\\test\\java"
    },
    "dirCount": 1,
    "fileCount": 0,
    "length": 0,
    "curDeep": 2
  },
  {
    "file": {
      "path": "J:\\FileUnit\\file_java\\file\\src\\test\\resources"
    },
    "dirCount": 1,
    "fileCount": 0,
    "length": 0,
    "curDeep": 2
  }
]
7.点睛之笔

写了老半天,现在把写的东西考到build.gradle里,当插件用 Groovy完全兼容java,所以,都考进去就行了,上面的所以都可以当脚本用 Gradle真是挺好玩的,以后有什么想偷懒的,写个java版的,在一贴,就能当脚本用 今天东西有点杂,好好消化消化吧,感觉发现了新天地

生成文件结构图.png

代码语言:javascript
复制
////--------------------------插件-------------------------------------------
apply plugin: ListDirPlugin//声明使用插件

listDir {//根据拓展参数来自定义文件夹
    path = 'J:\\FileUnit\\file_java\\file'
}

////--------------------------插件书写-------------------------------------------
public class Filer {
    private File file;//文件夹名
    private int dirCount = 1;//子文件夹数量---不包括自身
    private int fileCount;//文件的个数
    private long length; //文件夹大小
    //过滤器集合
    private transient ArrayList<FileFilter> mFilters = new ArrayList<>();

    public int curDeep;//节点深度

    public void addFilter(FileFilter countFilter) {
        mFilters.add(countFilter);
    }

    public File getFile() {
        return file;
    }

    public int getDirCount() {
        return dirCount;
    }

    public int getFileCount() {
        return fileCount;
    }

    public long getLength() {
        return length;
    }


    private transient FileNode root;//根节点

    public Filer(String rootPath) {
        file = new File(rootPath);
        root = new FileNode(file);
    }

    public void scan() {
        scan(root);
    }


    public void scan(FileNode node) {
        File file = node.file;
        if (file.isFile()) {//如果节点是文件
            return;
        }

        File[] files = file.listFiles();
        for (File f : files) {
            FileNode child = new FileNode(f);
            child.deep = curDeep;
            node.child.add(child);

            for (FileFilter filter : mFilters) {
                if (filter != null && filter.iCanGo(f)) {
                    filter.filter(child.file, child.deep);
                }
            }

            if (f.isDirectory()) {
                dirCount++;//每调用一次说明有一个文件夹
                curDeep++;
                scan(child);
            } else {
                fileCount++;//每调用一次说明有一个文件
                length += f.length();
            }
        }
        curDeep--;
    }

    /**
     * 将Filer的数据进行初始化
     * 遍历当前节点
     *
     * @param target
     * @return
     */
    private long traverse(FileNode target) {//--------瞎猫碰到死耗子
        if (target.child == null) {
            return length;
        }
        if (target.file.isDirectory()) {
            dirCount++;//每调用一次说明有一个文件夹
            for (FileNode node : target.child) {
                length += traverse(node);
            }
            return length;
        } else {
            fileCount++;//每调用一次说明有一个文件
            return length + target.file.length();
        }
    }

    /**
     * 文件节点
     */
    private class FileNode {
        public ArrayList<FileNode> child;//子节点集合
        public File file; //文件路径
        public int deep;//深度

        public FileNode(File file) {
            this.file = file;
            child = new ArrayList<>(4);
        }
    }

    @Override
    public String toString() {
        return "Filer{" +
                "file=" + file +
                ", dirCount=" + dirCount +
                ", fileCount=" + fileCount +
                ", length=" + length +
                ", curDeep=" + curDeep +
                '}';
    }
}

/**
 * 作者:张风捷特烈
 * 时间:2019/2/13/013:14:31
 * 邮箱:1981462002@qq.com
 * 说明:文件过滤接口
 */
public interface FileFilter {
    /**
     * 根据路径判断是否过滤出
     * @param path 路径
     * @return 是否可以执行filter
     */
    boolean iCanGo(File path);

    /**
     * 过滤的逻辑操作
     * @param file 文件
     * @param deep 该文件深度
     */
    void filter(File file, int deep);
}


/**
 * 作者:张风捷特烈
 * 时间:2019/2/13/013:10:59
 * 邮箱:1981462002@qq.com
 * 说明:创建目录结构
 */
public class StructureBuilder implements FileFilter {

    String prefix;
    String blank;
    StringBuilder sb = new StringBuilder("目录结构:\n");

    public String getStructure() {
        return sb.toString();
    }

    public StructureBuilder() {
        this("|---", "····");
    }

    public StructureBuilder(String prefix, String blank) {
        this.prefix = prefix;
        this.blank = blank;
    }

    @Override
    public boolean iCanGo(File file) {
        return true;
    }

    @Override
    public void filter(File file, int deep) {
        sb
                .append(blankBuilder(blank, deep))
                .append(prefix)
                .append(file.getName())
                .append("\n");

    }

    private static StringBuilder blankBuilder(String symbol, int num) {
        StringBuilder stringBuffer = new StringBuilder();
        for (int i = 0; i < num; i++) {
            stringBuffer.append(symbol);
        }
        return stringBuffer;
    }

    /**
     * 将文件结构写入文件
     * @param file 文件
     */
    public  void writeFile(File file) {
        FileWriter fw = null;
        try {
            fw = new FileWriter(file);
            String time = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss", Locale.CHINA)
                    .format(System.currentTimeMillis());
            fw.write("张风捷特烈--修改于" + time + "\n");
            fw.write(sb.toString());

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

//----------------------------以下是插件部分--------------------------------
class ListDirPlugin implements Plugin<Project> {
    //该接口定义了一个apply()方法,在该方法中,我们可以操作Project,
    //比如向其中加入Task,定义额外的Property等。
    void apply(Project project) {
        //加载Extension
        project.extensions.create("listDir", MkDirPluginPluginExtension)

        //使用Extension配置信息
        project.task('listDirTask') << {
            String path = project.listDir.path
            Filer filer = new Filer(path);
            StructureBuilder structureBuilder = new StructureBuilder("|---","....");
            filer.addFilter(structureBuilder);
            filer.scan();
            System.out.println(structureBuilder.getStructure());

            File file = new File(filer.getFile(), "structure.txt");
            structureBuilder.writeFile(file);
        }

    }
}

class MkDirPluginPluginExtension {//拓展参数
    String path = ''
}

后记:捷文规范
1.本文成长记录及勘误表

项目源码

日期

附录

V0.1--

2018-2-13

发布名:杂篇-从文件操作来发起的杂谈[-File-] 捷文链接:https://www.jianshu.com/u/e4e52c116681

2.更多关于我

笔名

QQ

微信

张风捷特烈

1981462002

zdl1994328

我的github:https://github.com/toly1994328 我的简书:https://www.jianshu.com/u/e4e52c116681 我的掘金:https://juejin.im/user/5b42c0656fb9a04fe727eb37 个人网站:http://www.toly1994.com

3.声明

1----本文由张风捷特烈原创,转载请注明 2----欢迎广大编程爱好者共同交流 3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正 4----看到这里,我在此感谢你的喜欢与支持

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.02.13 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 零、前言
  • 一、创建Filer类
    • 1.先举一个简单的例子:目录结构如下:
      • 2.Filer信息封装
        • 3.定义文件节点类
          • 4.Filer的初始化
            • 5.文件的扫描
              • 6.既然一个一个节点连接了这颗树
                • 7.调用
                • 二、全副武装
                  • 1.统计文件夹中文件类型
                    • 2.策略的出现
                      • 3.当需要修改时,分离的优势显现
                        • 4.抽象与拓展
                          • 5.基于接口的功能拓展
                            • 6.批量修改操作
                              • 7.多个过滤器
                              • 三、言归正传
                                • 1.添加节点深度字段并维护
                                  • 2.修改接口
                                    • 3.打印目录结构
                                      • 4.保存文件目录树
                                        • 5.创建json格式的文件描述文件夹
                                          • 6.还是自己优化一下json吧
                                            • 7.点睛之笔
                                            • 后记:捷文规范
                                              • 1.本文成长记录及勘误表
                                                • 2.更多关于我
                                                  • 3.声明
                                                  领券
                                                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档