雜篇-從整理文件發起的雜談[-File-]

有些東西很簡單,簡單到你不想去想,好比:爲何天是藍的?--侷限物語java

零、前言

說一下本篇的初衷: coder盤做爲工做盤有點亂,想整理一下
也想尋求一個方便管理工程的點子,既然File類玩的倒背如流,何妨玩一下
加之File的自然遞歸性,再熟悉一下遞歸也不錯,因此便有此篇,
本文前奏有點長,好戲在後面----注意:本文的重點不是操做,而是思想node

coder.png


1、建立Filer類

java的File對象只是提供一些基本信息,這也是情理之中
感受能夠封裝一下,提供更多的信息,好比下面的,文件夾大小,子文件個數等git

信息.png


1.先舉一個簡單的例子:目錄結構以下:
|---edite
    |---top
    |---toly
        |---界面編程GUI
            |---Edit.java
            |---FileHelper.java
            |---IOUtils.java
    |---edit.jar
    |---main.txt
複製代碼

2.Filer信息封裝

先實現子文件夾、文件的個數、文件夾大小三個屬性github

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個(極可能一個文件夾裏就一兩個文件)編程

/**
 * 文件節點
 */
private class FileNode {
    public ArrayList<FileNode> child;//子節點集合
    public File file; //文件路徑
    public FileNode(File file) {
        this.file = file;
        child = new ArrayList<>(4);
    }
}
複製代碼

4.Filer的初始化
private FileNode root;//根節點
 public Filer(String rootPath) {
     file = new File(rootPath);
     root = new FileNode(file);//初始化根節點
 }
複製代碼

5.文件的掃描

你能夠結合下面的分析,本身debug走一走json

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

就這樣一個樹形的結構就造成了bash

debug查看一下.png


6.既然一個一個節點鏈接了這顆樹

那麼徹底能夠在掃描的時候多作一些事,好比維護那三個成員變量服務器

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

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


2、全副武裝

1.統計文件夾中文件類型

想一下,花了7秒獲得三個屬性,並且把文件都遍歷一邊了,感受有點虧
如今想看一下有多少種文件,這要求不過度吧,想一想也簡單,看一下後綴名就好了

打印.png

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

/**
 * 做者:張風捷特烈
 * 時間: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裏好些

/**
 * 做者:張風捷特烈
 * 時間: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

/**
 * 做者:張風捷特烈
 * 時間: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

/**
 * 做者:張風捷特烈
 * 時間: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用來操做...一個控制系法師,一個輸出系戰士,吊打無誤

固然你也能夠將類型變成參數,經過構造來,這樣會更方便

/**
 * 做者:張風捷特烈
 * 時間: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文件結尾加一行字的需求:
怎麼操做我說了算,甚至不用新建類,寫個匿名內部類就好了,拿到文件,就寫唄!

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.多個過濾器

反正遍歷都遍歷了,不用白不用,多幾個過濾器,多幾重操做

//過濾器集合
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);
}
複製代碼

3、言歸正傳

1.添加節點深度字段並維護

每當文件夾時curDeep++,跳出一次scan方法時curDeep--

打印節點深度.png

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這個參數傳出去...有兩個方法,一種:修改接口!!!
第二種:新建接口,還好實現類不是不少,這裏改一下接口吧...

/**
 * 做者:張風捷特烈
 * 時間: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

-----------------建立目錄結構過濾器----------------
|--這裏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

---->[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在手,去解析着玩吧

Filer filer = new Filer("J:\\edite");
filer.scan();
String json = new GsonBuilder().setPrettyPrinting().create().toJson(filer);
System.out.println(json);
複製代碼
{
  "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字段屏蔽掉,看一下本項目目錄信息吧
保存到文件夾裏也是同樣的,這裏就不演示了

/**
 * 做者:張風捷特烈
 * 時間: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();
            }
        }
    }
}
複製代碼
[
  {
    "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

////--------------------------插件-------------------------------------------
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-]
捷文連接:www.jianshu.com/u/e4e52c116…

2.更多關於我
筆名 QQ 微信
張風捷特烈 1981462002 zdl1994328

個人github:github.com/toly1994328
個人簡書:www.jianshu.com/u/e4e52c116…
個人掘金:juejin.im/user/5b42c0…
我的網站:www.toly1994.com

3.聲明

1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大編程愛好者共同交流
3----我的能力有限,若有不正之處歡迎你們批評指證,一定虛心改正
4----看到這裏,我在此感謝你的喜歡與支持

icon_wx_200.png
相關文章
相關標籤/搜索