Java'da dosyaları özyinelemeli olarak listeleme


258

Java'da bir dizin altındaki tüm dosyaları özyinelemeli olarak nasıl listeleyebilirim? Çerçeve herhangi bir fayda sağlıyor mu?

Çok çılgın uygulamalar gördüm. Ama çerçeve veya nio'dan hiçbiri


2
Yanıtların birçoğu için performans testleri sağlayan Test Sonuçlarını yeni tamamladım . Şaşırtıcı olmayan bir şekilde tüm NIO tabanlı cevaplar en iyi performansı verir. Commons-io yanıtı, çalışma uzunluğunun iki katından fazla olan en kötü performanstır.
Brett Ryan

2
Java8: Files.walk?
Benj

Yanıtlar:


327

Java 8, ağaçtaki tüm dosyaları işlemek için güzel bir akış sağlar.

Files.walk(Paths.get(path))
        .filter(Files::isRegularFile)
        .forEach(System.out::println);

Bu, dosyaları taramak için doğal bir yol sağlar. Bir akış olduğundan, sonuçta limit, gruplama, haritalama, erken çıkış vb.Gibi tüm güzel akış işlemlerini yapabilirsiniz.

GÜNCELLEME : Ben de orada işaret olabilir files.Find bir sürer BiPredicate dosya özelliklerini kontrol etmek için gerekirse daha verimli olabilir.

Files.find(Paths.get(path),
           Integer.MAX_VALUE,
           (filePath, fileAttr) -> fileAttr.isRegularFile())
        .forEach(System.out::println);

JavaDoc, bu yöntemin Files.walk'tan daha verimli olabileceğinden kaçınıyor olsa da, etkili bir şekilde aynıdır, ancak filtrenizdeki dosya özniteliklerini de alıyorsanız, performans farkı gözlemlenebilir. Sonunda, özniteliklere filtre uygulamanız gerekiyorsa Files.find kullanın , aksi takdirde Files.walk'u kullanın , çünkü çoğunlukla aşırı yükler vardır ve daha uygundur.

TESTLER : İstediğim gibi birçok cevabın performans karşılaştırmasını yaptım. Sonuçları ve bir test senaryosunu içeren Github projesine göz atın .


6
Yeni başlayanlar için bile fonksiyonel programlamanın büyüsünü gösterebilen bu örneklerden biri.
Johnny

2
Bunun performansı java 8 öncesi yöntemlerle nasıl karşılaştırılır? Geçerli dizin geçişim çok yavaş ve hızlandıracak bir şey arıyorum.
Sridhar Sarnobat

1
Verilen cevapların çoğunu içeren bazı testler yazıyorum. Şimdiye kadar, Files.walkparalel bir akışla kullanmak en iyisi gibi görünüyor , bunu yakından takip Files.walkFileTreeeden marjinal olarak daha yavaş. Commons-io kullanarak kabul edilen cevap, testlerimin 4 kat daha yavaş olmasıyla açık ara en yavaştır.
Brett Ryan

1
@BrettRyan, çözümünüzü denedim ama bir istisna alıyorum Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException. Nasıl düzeltebilirim
Kachna

5
Bundan gerçek bir dosya listesini nasıl alabilirim?
thouliha

159

FileUtils var iterateFilesve listFilesyöntemler. Bir deneyin. ( commons-io'dan )

Düzenleme: Burada farklı yaklaşımların bir karşılaştırması için kontrol edebilirsiniz . Müşterek yaklaşım yavaş görünüyor, bu yüzden buradan daha hızlı olanlardan bazılarını seçin (önemliyse)


40
FYI / TLDR: Filtreleme olmadan tüm dosyaları özyinelemeli olarak listelemek istiyorsanız FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE), diryapın, temel dizine işaret eden bir File nesnesi.
andronikus

2
Sen kullanarak düşünebilirsiniz listFilesAndDirs()olarak, listFiles()boş klasörleri döndürmez.
schnatterer

1
@MikeFHay FileUtils koduna bakarak, bunun olabileceğini düşünüyorum FileUtils.listFiles(dir, true, true). using FileUtils.listFiles(dir, null, true)bir İstisna fırlatırken, FileUtils.listFiles(dir, true, null)tüm dosyaları alt dizinlere bakmadan listeler.
ocramot

JDK yerel kütüphanesine ne dersiniz? Bunu kolay bir şekilde uygulayabilirim ama diğer yerlerden C&P olurdum
Christian Bongiorno

1
Bazı testleri bir araya getiriyorum, ancak şimdiye kadar bu JDK8 veya JDK7 alternatiflerini kullanmaktan 4 kat daha yavaş çalışıyor gibi görünüyor. Symlinks ayrıca bu yaklaşımla sorunlu olduğunu kanıtladı, özellikle ağaçtaki daha yüksek dizinlere bağlandıklarında, bu yöntemin asla geri dönmemesine neden olur, bu filtreyi tutarak önlenebilir, ancak maalesef sembollerin kendileri bile ziyaret edilmez bir dosya.
Brett Ryan

138

// Koşmak için hazır

import java.io.File;

public class Filewalker {

    public void walk( String path ) {

        File root = new File( path );
        File[] list = root.listFiles();

        if (list == null) return;

        for ( File f : list ) {
            if ( f.isDirectory() ) {
                walk( f.getAbsolutePath() );
                System.out.println( "Dir:" + f.getAbsoluteFile() );
            }
            else {
                System.out.println( "File:" + f.getAbsoluteFile() );
            }
        }
    }

    public static void main(String[] args) {
        Filewalker fw = new Filewalker();
        fw.walk("c:\\" );
    }

}

9
Sadece yol hiyerarşisinde daha yüksek bir yola işaret eden sembolik bağlantılar için yöntemin hiç bitmemesine neden olacağını unutmayın. İşaret eden bir sembolik bağlantısı olan bir yol düşünün -> ..
Brett Ryan

2
Bu aslında Files.walkFileTree'nin kötü bir uygulamasıdır. Ben insanlar kendiniz yuvarlamaya çalışmak yerine FIles.walkFileTree bakmak öneriyoruz ... Bu tam sorun @BrettRyan işaret etti.
Tyler Nichols

Java.io.File dosyasını içe aktardığınız için teşekkür ederiz. Pek çok örnek, örneği bir keşif yolculuğunda bir başlangıç ​​noktası haline getiren ad alanı öğelerini ve hatta veri türü öğelerini eklemeyi unutur. Burada bu örnek çalıştırılmaya hazırdır. Teşekkürler.
barrypicker

Yol, Filewalker dosyasının bulunduğu yere bağlı olarak değişebilir. Kullanım "/", "./"ya "../"sırasıyla kök dizininde, geçerli çalışma dizini ve üst dizini için
Musa Kirathe

67

Java 7 olacak sahiptir Files.walkFileTree :

Bir başlangıç ​​noktası ve bir dosya ziyaretçisi sağlarsanız, dosya ziyaretçisinde dosya ağacında dolaşırken çeşitli yöntemleri çağırır. Kullanıcıların, özyinelemeli bir kopya, özyinelemeli bir hareket, özyinelemeli silme veya her bir dosyada izinleri ayarlayan veya başka bir işlem gerçekleştiren özyinelemeli bir işlem geliştirmeleri durumunda bunu kullanmasını bekliyoruz.

Artık bu soruya ilişkin tüm bir Oracle eğitimi var .


Ve asla yürüyüşün sonunu bildirmez.
Farid

25

Harici kütüphaneye gerek yok.
Aramadan sonra istediğinizi yapabilmeniz için bir Koleksiyon döndürür.

public static Collection<File> listFileTree(File dir) {
    Set<File> fileTree = new HashSet<File>();
    if(dir==null||dir.listFiles()==null){
        return fileTree;
    }
    for (File entry : dir.listFiles()) {
        if (entry.isFile()) fileTree.add(entry);
        else fileTree.addAll(listFileTree(entry));
    }
    return fileTree;
}

basit ve temiz
Leo

17

Gibi bir şey gitmek istiyorum:

public void list(File file) {
    System.out.println(file.getName());
    File[] children = file.listFiles();
    for (File child : children) {
        list(child);
    }
}

System.out.println dosyasıyla bir şey yapmayı göstermek için orada. normal bir dosya sıfır çocuğa sahip olacağından dosyalar ve dizinler arasında ayrım yapmaya gerek yoktur.


6
Dokümantasyonundan listFiles(): “Bu soyut yol adı bir dizini göstermiyorsa, bu yöntem geri döner null.”
hfs

Geliştirilmiş değişken genel statik Koleksiyonu <Dosya> listFileTree (Dosya dizini) {if (null == dir ||! Dir.isDirectory ()) {return Collections.emptyList (); } final Set <File> fileTree = yeni HashSet <File> (); for (Dosya girişi: dir.listFiles ()) {if (entry.isFile ()) {fileTree.add (entry); } else {fileTree.addAll (listFileTree (giriş)); }} dönüş fileTree; }
Ben

Bana göre bu özyinelemeli en özlü cevaptır.
WillieT

14

Ben bu tür basit geçiş için özyineleme üzerinde bir kuyruk kullanmayı tercih ederim:

List<File> allFiles = new ArrayList<File>();
Queue<File> dirs = new LinkedList<File>();
dirs.add(new File("/start/dir/"));
while (!dirs.isEmpty()) {
  for (File f : dirs.poll().listFiles()) {
    if (f.isDirectory()) {
      dirs.add(f);
    } else if (f.isFile()) {
      allFiles.add(f);
    }
  }
}

Ancak algoritmanız girintili çıktı ile yazdıramıyor. Dizinler ve dosyalar dağınık. Herhangi bir çözüm?
Wei

12

basit özyineleme kullanarak kendiniz yazın:

public List<File> addFiles(List<File> files, File dir)
{
    if (files == null)
        files = new LinkedList<File>();

    if (!dir.isDirectory())
    {
        files.add(dir);
        return files;
    }

    for (File file : dir.listFiles())
        addFiles(files, file);
    return files;
}

1
Lütfen! arayanın dosyalar listesini başlatmasına izin verin, böylece her seferinde boşluğunu kontrol etmek zorunda kalmaz. Listeyi oluşturan ikinci (genel) bir yöntem oluşturmak istiyorsanız, bu dahili yöntemi çağırır ve listenin tamamını döndürür.
helios

1
her neyse. null kontrol çok pahalı değil, kolaylık + kişisel tercih kenara o alırsınız düşünüyorum.
pstanton

Biraz daha ayrıntılı açıklayabilir misiniz?
uday

8

Bunun işi yapması gerektiğini düşünüyorum:

File dir = new File(dirname);
String[] files = dir.list();

Bu şekilde dosya ve dizinleriniz olur. Şimdi özyineleme kullanın ve dirs için aynısını yapın ( Filesınıf isDirectory()yöntemi vardır).


8

Java 7 ile aşağıdaki sınıfı kullanabilirsiniz:

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

public class MyFileIterator extends SimpleFileVisitor<Path>
{
    public MyFileIterator(String path) throws Exception
    {
        Files.walkFileTree(Paths.get(path), this);
    }

    @Override
    public FileVisitResult visitFile(Path file,
            BasicFileAttributes attributes) throws IOException
    {
        System.out.println("File: " + file);
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult preVisitDirectory(Path dir,
            BasicFileAttributes attributes) throws IOException
    {
        System.out.println("Dir: " + dir);
        return FileVisitResult.CONTINUE;
    }
}

7

Java 8'de artık bir dosya ağacında yürümek için Dosyalar yardımcı programını kullanabiliriz. Çok basit.

Files.walk(root.toPath())
      .filter(path -> !Files.isDirectory(path))
      .forEach(path -> System.out.println(path));

7

Bu kod çalıştırılmaya hazır

public static void main(String... args) {
    File[] files = new File("D:/").listFiles();
    if (files != null) 
       getFiles(files);
}

public static void getFiles(File[] files) {
    for (File file : files) {
        if (file.isDirectory()) {
            getFiles(file.listFiles());
        } else {
            System.out.println("File: " + file);
        }
    }
}

4

Özyinelemeli geçişin dışında Ziyaretçi bazlı bir yaklaşım da kullanılabilir.

Aşağıdaki kod, geçiş için Ziyaretçi tabanlı bir yaklaşım kullanır.Programa girişin geçilecek kök dizin olması beklenir.

public interface Visitor {
    void visit(DirElement d);
    void visit(FileElement f);
}

public abstract class Element {
    protected File rootPath;
    abstract void accept(Visitor v);

    @Override
    public String toString() {
        return rootPath.getAbsolutePath();
    }
}

public class FileElement extends Element {
    FileElement(final String path) {
        rootPath = new File(path);
    }

    @Override
    void accept(final Visitor v) {
        v.visit(this);
    }
}

public class DirElement extends Element implements Iterable<Element> {
    private final List<Element> elemList;
    DirElement(final String path) {
        elemList = new ArrayList<Element>();
        rootPath = new File(path);
        for (File f : rootPath.listFiles()) {
            if (f.isDirectory()) {
                elemList.add(new DirElement(f.getAbsolutePath()));
            } else if (f.isFile()) {
                elemList.add(new FileElement(f.getAbsolutePath()));
            }
        }
    }

    @Override
    void accept(final Visitor v) {
        v.visit(this);
    }

    public Iterator<Element> iterator() {
        return elemList.iterator();
    }
}

public class ElementWalker {
    private final String rootDir;
    ElementWalker(final String dir) {
        rootDir = dir;
    }

    private void traverse() {
        Element d = new DirElement(rootDir);
        d.accept(new Walker());
    }

    public static void main(final String[] args) {
        ElementWalker t = new ElementWalker("C:\\temp");
        t.traverse();
    }

    private class Walker implements Visitor {
        public void visit(final DirElement d) {
            System.out.println(d);
            for(Element e:d) {
                e.accept(this);
            }
        }

        public void visit(final FileElement f) {
            System.out.println(f);
        }
    }
}

3

Belirli bir klasör veya dizindeki dosyaların listesini özyineli olarak almak için aşağıdaki kodu kullanabilirsiniz.

public static void main(String args[]) {

        recusiveList("D:");

    }

    public static void recursiveList(String path) {

        File f = new File(path);
        File[] fl = f.listFiles();
        for (int i = 0; i < fl.length; i++) {
            if (fl[i].isDirectory() && !fl[i].isHidden()) {
                System.out.println(fl[i].getAbsolutePath());
                recusiveList(fl[i].getAbsolutePath());
            } else {
                System.out.println(fl[i].getName());
            }
        }
    }

2

Kabul edilen cevap harika, ancak lambda içinde IO yapmak istediğinizde yıkılıyor.

Eyleminiz IOExceptions'ı bildirirse neler yapabileceğiniz aşağıda açıklanmıştır.

Filtrelenen akışı bir olarak ele alabilir Iterableve ardından eyleminizi her biri için düzenli bir döngüde yapabilirsiniz. Bu şekilde, bir lambda içindeki istisnaları ele almak zorunda kalmazsınız.

try (Stream<Path> pathStream = Files.walk(Paths.get(path))
        .filter(Files::isRegularFile)) {

    for (Path file : (Iterable<Path>) pathStream::iterator) {
        // something that throws IOException
        Files.copy(file, System.out);
    }
}

Bu numarayı burada buldum: https://stackoverflow.com/a/32668807/1207791


1

Tek bir listeyle özyinelemesiz BFS (özel örnek * .eml dosyalarını arıyor):

    final FileFilter filter = new FileFilter() {
        @Override
        public boolean accept(File file) {
            return file.isDirectory() || file.getName().endsWith(".eml");
        }
    };

    // BFS recursive search
    List<File> queue = new LinkedList<File>();
    queue.addAll(Arrays.asList(dir.listFiles(filter)));

    for (ListIterator<File> itr = queue.listIterator(); itr.hasNext();) {
        File file = itr.next();
        if (file.isDirectory()) {
            itr.remove();
            for (File f: file.listFiles(filter)) itr.add(f);
        }
    }

1

Benim sürümüm (tabii ki Java 8 ;-) yerleşik yürüyüş kullanmış olabilir):

public static List<File> findFilesIn(File rootDir, Predicate<File> predicate) {
        ArrayList<File> collected = new ArrayList<>();
        walk(rootDir, predicate, collected);
        return collected;
    }

    private static void walk(File dir, Predicate<File> filterFunction, List<File> collected) {
        Stream.of(listOnlyWhenDirectory(dir))
                .forEach(file -> walk(file, filterFunction, addAndReturn(collected, file, filterFunction)));
    }

    private static File[] listOnlyWhenDirectory(File dir) {
        return dir.isDirectory() ? dir.listFiles() : new File[]{};
    }

    private static List<File> addAndReturn(List<File> files, File toAdd, Predicate<File> filterFunction) {
        if (filterFunction.test(toAdd)) {
            files.add(toAdd);
        }
        return files;
    }

1

İşte basit ama mükemmel çalışan bir çözüm recursion:

public static List<Path> listFiles(String rootDirectory)
{
    List<Path> files = new ArrayList<>();
    listFiles(rootDirectory, files);

    return files;
}

private static void listFiles(String path, List<Path> collectedFiles)
{
    File root = new File(path);
    File[] files = root.listFiles();

    if (files == null)
    {
        return;
    }

    for (File file : files)
    {
        if (file.isDirectory())
        {
            listFiles(file.getAbsolutePath(), collectedFiles);
        } else
        {
            collectedFiles.add(file.toPath());
        }
    }
}

1
    private void fillFilesRecursively(File file, List<File> resultFiles) {
        if (file.isFile()) {
            resultFiles.add(file);
        } else {
            for (File child : file.listFiles()) {
                fillFilesRecursively(child, resultFiles);
            }
        }
    }

1

Tüm dosyaları / dosya adlarını tekrar tekrar yazdırmak için bunu buldum.

private static void printAllFiles(String filePath,File folder) {
    if(filePath==null) {
        return;
    }
    File[] files = folder.listFiles();
    for(File element : files) {
        if(element.isDirectory()) {
            printAllFiles(filePath,element);
        } else {
            System.out.println(" FileName "+ element.getName());
        }
    }
}

0

Örnek, java.nio dosyasından Files.find () kullanarak alt dizinlerde özyinelemeli aramada * .csv dosyaları çıkarır:

String path = "C:/Daten/ibiss/ferret/";
    logger.debug("Path:" + path);
    try (Stream<Path> fileList = Files.find(Paths.get(path), Integer.MAX_VALUE,
            (filePath, fileAttr) -> fileAttr.isRegularFile() && filePath.toString().endsWith("csv"))) {
        List<String> someThingNew = fileList.sorted().map(String::valueOf).collect(Collectors.toList());
        for (String t : someThingNew) {
            t.toString();
            logger.debug("Filename:" + t);
        }

    }

Bu örnekte, Bryan tarafından verilen # 1 örneğinde dosya adı parametresinin nasıl aktarılacağını anladığımda sorun yaşadığım için, Akış sonuçlarında foreach kullanarak -

Bu yardımcı olur umarım.


0

Kotlin FileTreeWalkbu amaçla var. Örneğin:

dataDir.walkTopDown().filter { !it.isDirectory }.joinToString("\n") {
   "${it.toRelativeString(dataDir)}: ${it.length()}"
}

Belirli bir kök altındaki tüm dizin dışı dosyaların, kök ve uzunluğa göre yolu olan satır başına bir dosya içeren bir metin listesi oluşturur.


0

Birisi zaten Java 8 yürüyüş sağlasanız bile yapabilirsiniz.

Bu, tüm dosyaları özyinelemeli olarak sağlayacaktır

  private Stream<File> files(File file) {
    return file.isDirectory()
            ? Arrays.stream(file.listFiles()).flatMap(this::files)
            : Stream.of(file);
}

-1

İstifleyici cevabına göre. Dış kütüphaneler olmadan JSP'de çalışan bir çözümdür, böylece sunucunuzda neredeyse her yere koyabilirsiniz:

<!DOCTYPE html>
<%@ page session="false" %>
<%@ page import="java.util.*" %>
<%@ page import="java.io.*" %>
<%@ page contentType="text/html; charset=UTF-8" %>

<%!
    public List<String> files = new ArrayList<String>();
    /**
        Fills files array with all sub-files.
    */
    public void walk( File root ) {
        File[] list = root.listFiles();

        if (list == null) return;

        for ( File f : list ) {
            if ( f.isDirectory() ) {
                walk( f );
            }
            else {
                files.add(f.getAbsolutePath());
            }
        }
    }
%>
<%
    files.clear();
    File jsp = new File(request.getRealPath(request.getServletPath()));
    File dir = jsp.getParentFile();
    walk(dir);
    String prefixPath = dir.getAbsolutePath() + "/";
%>

Sonra sadece şöyle bir şey yaparsınız:

    <ul>
        <% for (String file : files) { %>
            <% if (file.matches(".+\\.(apk|ipa|mobileprovision)")) { %>
                <li><%=file.replace(prefixPath, "")%></li>
            <% } %>
        <% } %>
    </ul>

1
Muhtemelen işe yarıyor olsa da, soru taranan dosyaların oluşturulmasıyla değil dosya taramasıyla ilgilidir. Algoritmanızı daha iyi ortaya çıkarın, iş mantığını bir JSP içine gömmek için önerilen bir uygulama değildir.
Samuel Kerrien

Bu ne yaptığınıza bağlıdır. Kurumsal boyutta bir uygulamada kesinlikle haklısınız. Bunu basit, bağımsız bir listeye bir damla olarak ihtiyacınız varsa, bu mükemmel.
Nux
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.