【问题标题】:Java - Iterate over all files in directoryJava - 遍历目录中的所有文件
【发布时间】:2018-04-14 18:21:00
【问题描述】:

我想在目录和嵌套子目录中找到所有txt 文件。如果找到,我想将其从一个位置移动到另一个位置。

如果我没有任何嵌套的子目录,下面的代码可以正常工作。

以下代码的问题是,一旦找到嵌套目录,它只会从那个特定的嵌套子目录返回文件。 但我想要我的目录(父目录及其嵌套的子目录)中的所有txt 文件。

public class FilesFindingInDirectory {
    static ArrayList<File> al = new ArrayList<File>();
    static File fileLocation = null;
    public static void main(String[] args) throws IOException {


        File filePath = new File("C:\\Users\\Downloads");

        File[] listingAllFiles = filePath.listFiles();

        ArrayList<File> allFiles = iterateOverFiles(listingAllFiles);


                for (File file : allFiles) {
                    if(file != null) {
                        String fileName = file.getName();

                        String sourceFilepath = file.getAbsolutePath();
                        File targetFilePath = new File("D:\\TestFiles");
                        String targetPath = targetFilePath.getPath();

                        Files.move(Paths.get(sourceFilepath), Paths.get("D:\\TestFiles\\" + fileName)); 
                    }

                }
            }


public static ArrayList<File> iterateOverFiles(File[] files) {


        for (File file : files) {

            if (file.isDirectory()) {

                iterateOverFiles(file.listFiles());// Calls same method again.

            } else {

                fileLocation = findFileswithTxtExtension(file);
                if(fileLocation != null) {
                    System.out.println(fileLocation);
                    al.add(fileLocation);
                }


            }
        }

        return al;
    }

public static File findFileswithTxtExtension(File file) {

        if(file.getName().toLowerCase().endsWith("txt")) {
            return file;
        }

        return null;
    }
}

【问题讨论】:

  • 尽管代码在递归结果收集部分可以改进,但我测试了你的代码,它工作正常。你能详细说明你看到什么问题吗?或者提供一个例子?我用一个包含 2 个 txt 文件的目录和另一个包含 2 个其他文本文件的目录测试了代码,它运行良好。我唯一能想象的是,如果文件名相同,它们最终会在最终目的地被覆盖。

标签: java file recursion


【解决方案1】:

您已经在使用 nio Files API 来移动文件,为什么不使用它来迭代文件?

 List<Path> txtFiles = Files.walk(Paths.get("C:\\Users\\Downloads"))
                            //use to string here, otherwise checking for path segments
                            .filter(p -> p.toString().endsWith(".txt"))
                            .collect(Collectors.toList());

如果您不需要该中间列表,您也可以在 foreach 终端操作中运行您的移动操作

Files.walk(Paths.get("C:\\Users\\Downloads"))
     .filter(p -> p.toString().endsWith(".txt"))
     .forEach(p -> {
        try {
            Files.move(p, Paths.get("D:\\TestFiles", p.getFileName().toString()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    });

【讨论】:

  • 我们的Files.find,它为过滤提供了一个参数。
  • Files#walk(Path) 的 OpenJDK 文档有一个 API 说明指出 “此方法必须在 try-with-resources 语句或类似的控制结构中使用,以确保流的打开目录是流的操作完成后立即关闭。” 这是否意味着有必要使用 try-with-resources 语句将代码包装在您的答案中?还是使用功能性 API/流满足此要求?
  • Oracle 的 SE 9 中似乎添加了上面的 API 说明。SE 8 Files#walk(Path, FileVisitOption) documentation 声明 "返回的流封装了一个或多个 DirectoryStream。如果需要及时处置文件系统资源,则应使用 try-with-resources 构造来确保在流操作完成后调用流的 close 方法。对已关闭的流进行操作将导致 IllegalStateException。"
【解决方案2】:

从你的递归函数中删除这一行:

return al;

将此行更改为仅调用递归函数:

ArrayList<File> allFiles = iterateOverFiles(listingAllFiles);

iterateOverFiles(listingAllFiles);

最后更改您的 for 循环以遍历静态字段 al。

for (File file : allFiles) {

for (File file : al) {

解释:有很多方法可以为这个问题编写递归。在这种情况下,您有一个用于收集结果的全局变量。每次迭代都应该添加到该全局结果中,然后简单地返回。在所有递归调用结束时,全局变量将包含所有结果。

【讨论】:

    【解决方案3】:

    您正确地递归调用了该函数,但随后您忽略了它的返回值。相反,您应该将其附加到结果列表中:

    public static List<File> iterateOverFiles(File[] files) {
        List<File> result = new ArrayList<>();
        for (File file : files) {
            if (file.isDirectory()) {
                result.addAll(iterateOverFiles(file.listFiles()); // Here!
            } else {
                fileLocation = findFileswithTxtExtension(file);
                if(fileLocation != null) {
                    result.add(fileLocation);
                }
            }
        }
    
        return result;
    }
    

    【讨论】:

      【解决方案4】:

      只需遍历一个目录,跳过任何非目录条目和没有所需扩展名的条目。将具有正确扩展名的所有文件添加到结果中,并为每个目录递归地执行此操作。

      public class FilesFindingInDirectory {
         public static void main(String[] args) throws IOException {
      
      
          File filePath = new File("C:\\Users\\Downloads");
      
      
          Collection<File> allFiles = findFiles(filePath, ".txt");
          allFiles.forEach(file -> {
                          String fileName = file.getName();
      
                          String sourceFilepath = file.getAbsolutePath();
                          File targetFilePath = new File("D:\\TestFiles");
                          String targetPath = targetFilePath.getPath();
      
                          Files.move(Paths.get(sourceFilepath), Paths.get("D:\\TestFiles\\" + fileName)); 
                      }
      
                  }
              }
      
      public static List<File> findFiles(File dir, String extension) {
          File[] files = dir.listFiles(f -> f.isDirectory() || f.getName().toLowerCase().endsWith(extension);
          ArrayList<File> result = new ArrayList<>();  
          if (files != null) {
             for (File file : files) {
      
              if (file.isDirectory()) {
                  result.addAll(findFiles(file, extension);
              } else {
                  result.add(file);
              }
      
          }
      
          return result;
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2012-06-15
        • 2015-03-05
        • 1970-01-01
        • 2018-10-18
        • 1970-01-01
        • 1970-01-01
        • 2017-06-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多