ฉันจะวนซ้ำไฟล์ในไดเรกทอรีใน Java ได้อย่างไร


175

ฉันต้องการรายชื่อของไฟล์ทั้งหมดในไดเรกทอรีรวมถึงไฟล์ในทุกไดเรกทอรีย่อย วิธีมาตรฐานในการทำซ้ำไดเรกทอรีด้วย Java คืออะไร?

คำตอบ:


207

คุณสามารถใช้File#isDirectory()เพื่อทดสอบว่าไฟล์ที่กำหนด (เส้นทาง) เป็นไดเรกทอรี หากเป็นเช่นนี้trueคุณเพียงเรียกวิธีเดียวกันอีกครั้งพร้อมFile#listFiles()ผลลัพธ์ นี้เรียกว่าการเรียกซ้ำ

นี่คือตัวอย่างกำหนดการพื้นฐาน

public static void main(String... args) {
    File[] files = new File("C:/").listFiles();
    showFiles(files);
}

public static void showFiles(File[] files) {
    for (File file : files) {
        if (file.isDirectory()) {
            System.out.println("Directory: " + file.getName());
            showFiles(file.listFiles()); // Calls same method again.
        } else {
            System.out.println("File: " + file.getName());
        }
    }
}

โปรดทราบว่านี่เป็นเรื่องละเอียดอ่อนStackOverflowErrorเมื่อต้นไม้ลึกกว่าสแต็กของ JVM ที่สามารถเก็บไว้ได้ คุณอาจต้องการใช้วิธีการวนซ้ำหรือเรียกซ้ำแบบหางปลาไหลแทน แต่นั่นเป็นอีกเรื่องหนึ่ง;)


ขอบคุณ Balus ความคิดเกี่ยวกับความลึกของการเดาทั่วไป?
James

10
ขึ้นอยู่กับการตั้งค่าหน่วยความจำของ JVM ของคุณ แต่โดยทั่วไปบางสิ่งบางอย่างเช่นไม่กี่พัน หากคุณคิดว่าคุณอาจเคยเจอไดเรกทอรีเช่นนั้นอย่าใช้การเรียกซ้ำ
Mike Baranczak

4
นี่คือความอ่อนแอNullPointerExceptionเมื่อระบบไฟล์เปลี่ยนไประหว่างการเรียกisDirectoryและlistFilesอาจเกิดขึ้นถ้าSystem.out.printlnบล็อกหรือคุณเพิ่งได้รับโชคร้ายจริงๆ การตรวจสอบว่าผลลัพธ์ของlistFilesไม่เป็นโมฆะจะแก้ไขเงื่อนไขการแข่งขันนั้น
Mike Samuel

1
@BoratSagdiyev, ไม่ได้ใช้ API ไฟล์ Java เก่า แต่ถ้าคุณอยู่ใน JVM ที่ทันสมัยแล้วมันjava.nio.file.DirectoryStreamจะอนุญาตให้คุณทำซ้ำในไดเรกทอรีและสามารถนำไปใช้กับหน่วยความจำขนาดเล็ก แต่วิธีเดียวที่จะบอกได้อย่างแน่นอน เพื่อตรวจสอบการใช้งานหน่วยความจำบนแพลตฟอร์มเฉพาะ
Mike Samuel

1
โฟลเดอร์ "C: \\" ไม่ใช่ตัวเลือกที่ดีที่สุด)
Vyacheslav

86

หากคุณกำลังใช้ Java 1.7 java.nio.file.Files.walkFileTree(...)คุณสามารถใช้

ตัวอย่างเช่น:

public class WalkFileTreeExample {

  public static void main(String[] args) {
    Path p = Paths.get("/usr");
    FileVisitor<Path> fv = new SimpleFileVisitor<Path>() {
      @Override
      public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
          throws IOException {
        System.out.println(file);
        return FileVisitResult.CONTINUE;
      }
    };

    try {
      Files.walkFileTree(p, fv);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}

หากคุณใช้ Java 8 คุณสามารถใช้สตรีมอินเตอร์เฟสได้ด้วยjava.nio.file.Files.walk(...):

public class WalkFileTreeExample {

  public static void main(String[] args) {
    try (Stream<Path> paths = Files.walk(Paths.get("/usr"))) {
      paths.forEach(System.out::println);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}

1
มีวิธีใดที่สตรีมจะใส่จุดตรวจสอบเมื่อมีการเดินไดเรกทอรีใหม่และเรียกใช้ฟังก์ชัน
Raghu DV

28

ตรวจสอบFileUtilsชั้นใน Apache คอมมอนส์ - เฉพาะiterateFiles :

อนุญาตให้วนซ้ำไฟล์ในไดเรกทอรีที่กำหนด (และอาจเป็นไดเรกทอรีย่อย)


5
API นี้ไม่ได้สตรีมอย่างแท้จริง (ถ้าคุณสนใจเกี่ยวกับการใช้ mem) มันสร้างคอลเลกชันขึ้นก่อนจากนั้นจะส่งคืนตัววนซ้ำมากกว่า: return listFiles (ไดเรกทอรี fileFilter, dirFilter) .iterator ();
Gili Nachum

ตัวเลือกที่ดีสำหรับ Java 1.6
David I.

เห็นด้วยกับ @GiliNachum FileUtils โดย Apache จะรวบรวมไฟล์ทั้งหมดก่อนและให้ตัววนซ้ำสำหรับไฟล์เหล่านั้น มันเป็นอันตรายต่อทรัพยากรถ้าคุณมีไฟล์จำนวนมาก
Bogdan Samondros

8

สำหรับ Java 7+ ยังมีhttps://docs.oracle.com/javase/7/docs/api/java/nio/file/DirectoryStream.html

ตัวอย่างที่นำมาจาก Javadoc:

List<Path> listSourceFiles(Path dir) throws IOException {
   List<Path> result = new ArrayList<>();
   try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.{c,h,cpp,hpp,java}")) {
       for (Path entry: stream) {
           result.add(entry);
       }
   } catch (DirectoryIteratorException ex) {
       // I/O error encounted during the iteration, the cause is an IOException
       throw ex.getCause();
   }
   return result;
}

8

การใช้ org.apache.commons.io.FileUtils

File file = new File("F:/Lines");       
Collection<File> files = FileUtils.listFiles(file, null, true);     
for(File file2 : files){
    System.out.println(file2.getName());            
} 

ใช้ false ถ้าคุณไม่ต้องการไฟล์จากไดเรกทอรีย่อย


3

มันเป็นต้นไม้ดังนั้นการเรียกซ้ำเป็นเพื่อนของคุณ: เริ่มต้นด้วยไดเรกทอรีหลักและเรียกวิธีการรับอาร์เรย์ของไฟล์ย่อย วนซ้ำผ่านอาร์เรย์เด็ก หากค่าปัจจุบันเป็นไดเรกทอรีให้ส่งผ่านไปยังการเรียกซ้ำของวิธีการของคุณ หากไม่มีให้ประมวลผลไฟล์ leaf อย่างเหมาะสม


2

ตามที่ระบุไว้นี่เป็นปัญหาการเรียกซ้ำ โดยเฉพาะอย่างยิ่งคุณอาจต้องการดู

listFiles() 

ใน Java API ไฟล์ที่นี่ มันส่งกลับอาร์เรย์ของไฟล์ทั้งหมดในไดเรกทอรี ใช้สิ่งนี้พร้อมกับ

isDirectory()

เพื่อดูว่าคุณจำเป็นต้องเรียกคืนเพิ่มเติมหรือไม่เป็นการเริ่มต้นที่ดี


ลิงก์นี้อาจมีการใช้งานเนื่องจากลิงก์ในคำตอบเสีย
Donglecow

0

หากต้องการเพิ่มด้วยคำตอบ @msandiford เนื่องจากเวลาส่วนใหญ่เมื่อทรีไฟล์ถูกเรียกใช้แล้วคุณอาจต้องการเรียกใช้ฟังก์ชันเป็นไดเรกทอรีหรือมีการเยี่ยมชมไฟล์ใดไฟล์หนึ่งโดยเฉพาะ หากคุณลังเลที่จะใช้สตรีม วิธีการแทนที่ต่อไปนี้สามารถนำมาใช้

Files.walkFileTree(Paths.get(Krawl.INDEXPATH), EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
    new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                throws IOException {
                // Do someting before directory visit
                return FileVisitResult.CONTINUE;
        }
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                throws IOException {
                // Do something when a file is visited
                return FileVisitResult.CONTINUE;
        }
        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc)
                throws IOException {
                // Do Something after directory visit 
                return FileVisitResult.CONTINUE;
        }
});

0

คุณยังสามารถใช้ File.list (FilenameFilter) ในทางที่ผิด (และตัวแปร) สำหรับการแวะผ่านไฟล์ โค้ดย่อและใช้งานได้ในเวอร์ชันจาวารุ่นแรกเช่น:

// list files in dir
new File(dir).list(new FilenameFilter() {
    public boolean accept(File dir, String name) {
        String file = dir.getAbsolutePath() + File.separator + name;
        System.out.println(file);
        return false;
    }
});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.