c 遍历文件 递归遍历_将递归文件系统遍历转换为流
c 遍歷文件 遞歸遍歷
在學習編程的時候,回溯到Turbo Pascal的時代,我設法使用FindFirst , FindNext和FindClose函數在目錄中列出文件。 首先,我想出了一個打印給定目錄內容的過程。 您可以想象我為能夠真正從自身調用該過程以遞歸遍歷文件系統而感到自豪。 好吧,那時我還不知道遞歸一詞,但是它確實有用。 Java中的類似代碼如下所示:
不知道File.listFiles()可以返回null ,是嗎? 這就是它發出I / O錯誤的信號,就像IOException不存在一樣。 但這不是重點。 System.out.println()很少是我們所需要的,因此該方法既不可重用也不可組合。 這可能是“ 打開/關閉”原理的最佳反例。 我可以想象文件系統遞歸遍歷的幾個用例:
上面的每個用例都有一系列獨特的挑戰。 例如,我們不想建立所有文件的列表,因為在開始處理它之前將花費大量的時間和內存。 我們希望通過流水線計算(但沒有笨拙的訪問者模式)來處理文件的發現和延遲。 另外,我們也希望使搜索短路以避免不必要的I / O。 幸運的是,在Java 8中,其中一些問題可以通過流解決:
final File home = new File(FileUtils.getUserDirectoryPath()); final Stream<Path> files = Files.list(home.toPath()); files.forEach(System.out::println);請記住, Files.list(Path) (Java 8中的新增功能)沒有研究子目錄,我們將在以后進行修復。 這里最重要的一課是: Files.list()返回Stream<Path> –我們可以傳遞,組合,映射,過濾等的值。它非常靈活,例如,計算我擁有的文件數非常簡單在每個擴展名的目錄中:
import org.apache.commons.io.FilenameUtils;//...final File home = new File(FileUtils.getUserDirectoryPath()); final Stream<Path> files = Files.list(home.toPath()); final Map<String, List<Path>> byExtension = files.filter(path -> !path.toFile().isDirectory()).collect(groupingBy(path -> getExt(path)));byExtension.forEach((extension, matchingFiles) ->System.out.println(extension + "\t" + matchingFiles.size()));//...private String getExt(Path path) {return FilenameUtils.getExtension(path.toString()).toLowerCase(); }好吧,您可能會說,只是另一個API。 但是一旦我們需要更深入 ,遞歸地遍歷子目錄,它就會變得非常有趣。 流的一項驚人功能是,您可以通過各種方式將它們相互組合。 老Scala說“ flatMap that shit”也適用于此,請查看以下遞歸Java 8代碼:
//WARNING: doesn't compile, yet:private static Stream<Path> filesInDir(Path dir) {return Files.list(dir).flatMap(path ->path.toFile().isDirectory() ?filesInDir(path) :singletonList(path).stream()); }由filesInDir()延遲生成的Stream<Path>包含目錄中的所有文件,包括子目錄。 您可以通過調用map() , filter() , anyMatch() , findFirst()等將其用作任何其他流。但是它實際上如何工作? flatMap()與map()類似,但是map()是直接的1:1轉換, flatMap()允許用多個條目替換輸入Stream中的單個條目。 如果使用map() ,則最終會得到Stream<Stream<Path>> (或者可能是Stream<List<Path>> )。 但是flatMap()以爆炸內部條目的方式展平了該結構。 讓我們看一個簡單的例子。 想象一下Files.list()返回了兩個文件和一個目錄。 對于文件, flatMap()隨該文件接收一個元素的流。 我們不能簡單地返回該文件,而必須對其進行包裝,但實際上這是無操作的。 對于目錄,它變得更加有趣。 在這種情況下,我們遞歸地調用filesInDir() 。 結果,我們獲得了該目錄的內容流,并將其注入到外部流中。
上面的代碼簡短,甜美,并且…無法編譯。 這些令人討厭的異常再次檢查。 這是一個固定的代碼,包裝檢查過的異常以保持理智:
public static Stream<Path> filesInDir(Path dir) {return listFiles(dir).flatMap(path ->path.toFile().isDirectory() ?filesInDir(path) :singletonList(path).stream()); }private static Stream<Path> listFiles(Path dir) {try {return Files.list(dir);} catch (IOException e) {throw Throwables.propagate(e);} }不幸的是,這種相當優雅的代碼還不夠懶。 flatMap()急切求值,因此即使我們幾乎不要求第一個文件,它始終會遍歷所有子目錄。 您可以嘗試使用我的小型LazySeq庫,該庫試圖提供甚至更lazy-seq抽象,類似于Scala中的流或Clojure中的lazy-seq 。 但是,即使是標準的JDK 8解決方案也可能確實有用,并且可以大大簡化您的代碼。
翻譯自: https://www.javacodegeeks.com/2014/07/turning-recursive-file-system-traversal-into-stream.html
c 遍歷文件 遞歸遍歷
總結
以上是生活随笔為你收集整理的c 遍历文件 递归遍历_将递归文件系统遍历转换为流的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 成为Java流大师–第3部分:终端操作
- 下一篇: ai里面解组的快捷键(ai群组和解组的快