雜篇-從整理檔案發起的雜談[-File-]
有些東西很簡單,簡單到你不想去想,比如:為什麼天是藍的?--侷限物語
零、前言
說一下本篇的初衷: coder盤作為工作盤有點亂,想整理一下
也想尋求一個方便管理工程的點子,既然File類玩的滾瓜爛熟,何妨玩一下
加之File的天然遞迴性,再熟悉一下遞迴也不錯,所以便有此篇,
本文前奏有點長,好戲在後面----注意:本文的重點不是操作,而是思想

coder.png
一、建立Filer類
java的File物件只是提供一些基本資訊,這也是情理之中
感覺可以封裝一下,提供更多的資訊,比如下面的,資料夾大小,子檔案個數等

資訊.png
1.先舉一個簡單的例子:目錄結構如下:
|---edite |---top |---toly |---介面程式設計GUI |---Edit.java |---FileHelper.java |---IOUtils.java |---edit.jar |---main.txt
2.Filer資訊封裝
先實現子資料夾、檔案的個數、資料夾大小三個屬性
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走一走
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.既然一個一個節點連線了這顆樹
那麼完全可以在掃描的時候多做一些事,比如維護那三個成員變數
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
二、全副武裝
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 * 郵箱:[email protected] * 說明:字尾名過濾器 */ 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 * 郵箱:[email protected] * 說明:字尾名過濾器 */ 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 * 郵箱:[email protected] * 說明:檔案過濾介面 */ public interface FileFilter { /** * 根據路徑判斷是否過濾出 * @param file 檔案 * @return 是否可以執行filter */ boolean iCanGo(File file); /** * 過濾的邏輯操作 * @param file 檔案 */ void filter(File file); } /** * 作者:張風捷特烈 * 時間:2019/2/13/013:13:44 * 郵箱:[email protected] * 說明:字尾名過濾器 */ 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 * 郵箱:[email protected] * 說明:獲取一個資料夾下所有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 * 郵箱:[email protected] * 說明:獲取一個資料夾下所有某型別檔案的路徑 */ 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); }
三、言歸正傳
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 * 郵箱:[email protected] * 說明:檔案過濾介面 */ 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.列印目錄結構
一直覺得目錄結構挺帥氣,今天總算自己弄出來了,可喜可賀,可喜可賀
你可以想一下,如果不用監聽,Filer裡又要雜七雜八寫一堆才能打出來,而且在scan方法裡
過一段時間,再看scan方法估計就暈了,所以,回撥可以很好解決這個問題

列印目錄結構.png
-----------------建立目錄結構過濾器---------------- |--這裡iCanGo返回true,也就是暢通無阻,相當於一個監聽器 /** * 作者:張風捷特烈 * 時間:2019/2/13/013:10:59 * 郵箱:[email protected] * 說明:建立目錄結構 */ 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 * 郵箱:[email protected] * 說明:建立目錄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 * 郵箱:[email protected] * 說明:檔案過濾介面 */ 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 * 郵箱:[email protected] * 說明:建立目錄結構 */ 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 檔案 */ publicvoid 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/p/075f846207a9
2.更多關於我
筆名 | 微信 | |
---|---|---|
張風捷特烈 | 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----看到這裡,我在此感謝你的喜歡與支援

icon_wx_200.png