Javaでファイルを再帰的にリストする


258

Javaのディレクトリにあるすべてのファイルを再帰的にリストするにはどうすればよいですか?フレームワークはユーティリティを提供しますか?

ハックな実装をたくさん見ました。しかし、フレームワークやnioからのどれも


2
多くの回答のパフォーマンステストを提供するテスト結果を完了しました。当然のことながら、すべてのNIOベースの回答が最高のパフォーマンスを発揮します。commons-ioの回答は、ランレングスが2倍を超え、明らかに最悪のパフォーマーです。
ブレットライアン

2
Java8:Files.walk?
Benj

回答:


327

Java 8は、ツリー内のすべてのファイルを処理するための素晴らしいストリームを提供します。

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

これにより、ファイルをトラバースする自然な方法が提供されます。それはストリームなので、制限、グループ化、マッピング、早期終了など、結果に対して素晴らしいストリーム操作をすべて実行できます。

UPDATE:私もそこにあるかもしれないと指摘Files.findかかるBiPredicateあなたがファイル属性をチェックする必要がある場合は、より効率的である可能性があります。

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

JavaDocは、Files.walkよりもこのメソッドの方が効率的である可能性があると言っていますが、実質的には同じですが、フィルター内でファイル属性も取得している場合は、パフォーマンスの違いが見られます。最後に、あなたは属性にフィルタする必要がある場合は使用しFiles.findを、そうでない場合は使用しFiles.walkを、主にオーバーロードがあり、より便利であるため、。

テスト:要求に応じて、多くの回答のパフォーマンス比較を提供しました。結果とテストケースを含むGithubプロジェクトを確認してください。


6
初心者でも関数型プログラミングの魔法を示すことができる例の1つ。
ジョニー

2
これのパフォーマンスは、Java 8以前のメソッドとどのように比較されますか?現在のディレクトリトラバーサルは遅すぎるため、高速化できるものを探しています。
Sridhar Sarnobat

1
提供された回答にほとんどのバリアントを含むいくつかのテストを書いています。これまでのところ、Files.walk並列ストリームと一緒に使用するのが最善であると思われ、その後Files.walkFileTreeはわずかに遅いだけです。commons-ioを使用して受け入れられた回答は、私のテストでは4倍遅いという断然遅いです。
ブレットライアン

1
@BrettRyan、私はあなたの解決策を試しましたが、例外が発生しException in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedExceptionます。どうすれば修正できますか
Kachna

5
これから実際のファイルのリストを取得するにはどうすればよいですか?
thouliha 2015年

159

FileUtilsiterateFilesおよびlistFilesメソッドあります。それらを試してみてください。(commons-ioから)

編集:さまざまなアプローチのベンチマークをここ確認できます。commons-ioアプローチは遅いようですので、ここからより速いものをいくつか選んでください(重要な場合)。


40
FYI / TLDR:フィルタリングなしで再帰的にすべてのファイルを一覧表示するFileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)場合dirは、doを実行します。ここで、は、ベースディレクトリを指すFileオブジェクトです。
andronikus

2
空のフォルダを返さないためlistFilesAndDirs()、の使用を検討してlistFiles()ください。
schnatterer 2014

1
@MikeFHay FileUtilsコードを見ると、そうなると思いますFileUtils.listFiles(dir, true, true)。を使用FileUtils.listFiles(dir, null, true)すると例外がスローされますが、FileUtils.listFiles(dir, true, null)サブディレクトリを調べずにすべてのファイルがリストされます。
ocramot 2014年

JDKネイティブライブラリはどうですか?これは簡単に実装できますが、他の場所のC&Pになるだけです
Christian Bongiorno 2014

1
私はいくつかのテストをまとめていますが、これまでのところ、JDK8またはJDK7の代替を使用する場合よりも4倍遅いようです。シンボリックリンクもこのアプローチで問題があることが判明しています。特に、ツリー内の上位のディレクトリにリンクしている場合、これによりメソッドが返されなくなります。これは、フィルターを処理することで回避できますが、残念ながらシンボリックリンク自体はアクセスされません。ファイル。
ブレットライアン

138

//実行する準備ができました

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
パス階層の上位のパスを指すシンボリックリンクの場合、メソッドが終了しないことに注意してください。を指すシンボリックリンクのあるパスを考え-> .ます。
ブレットライアン

2
これは本質的に、Files.walkFileTreeの悪い実装です。自分でロールするのではなく、FIles.walkFileTreeを見ることをお勧めします... @BrettRyanが指摘した正確な問題に対応しています。
タイラーニコルズ

import java.io.File;を含めていただきありがとうございます。多くの例では、名前空間の要素やデータ型の要素を含めることを忘れており、この例を発見の旅の出発点にしています。この例はすぐに実行できます。ありがとう。
バリーピッカー

パスは、Filewalkerファイルの場所によって異なります。ルートディレクトリ、現在の作業ディレクトリ、親ディレクトリには、それぞれ"/""./"または"../"を使用してください
Moses Kirathe

67

Java 7 Files.walkFileTree あります

開始点とファイルビジターを指定すると、ファイルツリー内のファイルをウォークスルーするときに、ファイルビジターでさまざまなメソッドが呼び出されます。再帰的なコピー、再帰的な移動、再帰的な削除、またはアクセス許可を設定するか、各ファイルに対して別の操作を実行する再帰的な操作を開発している場合は、これを使用してください。

現在、この質問に関するOracleチュートリアル全体があります


そして、歩行の終了を通知することはありません。
ファリド

25

外部ライブラリは必要ありません。
呼び出しを行った後で、コレクションを自由に操作できるようにします。

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;
}

シンプルでクリーン
Leo

17

私は次のようなもので行きます:

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

System.out.printlnは、ファイルに対して何かを行うことを示すために存在します。通常のファイルには子がないため、ファイルとディレクトリを区別する必要はありません。


6
listFiles()「この抽象パス名がディレクトリを示さない場合、このメソッドはを返しますnull。」のドキュメントから。
hfs

改良されたバリアントpublic static Collection <File> listFileTree(File dir){if(null == dir ||!dir.isDirectory()){return Collections.emptyList(); } final Set <File> fileTree = new HashSet <File>(); for(File entry:dir.listFiles()){if(entry.isFile()){fileTree.add(entry); } else {fileTree.addAll(listFileTree(entry)); }} return fileTree; }
ベン

私にとって、これは再帰的な最も簡潔な答えです。
WillieT

14

この種の単純なトラバースには、再帰よりもキューを使用することを好みます。

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);
    }
  }
}

しかし、アルゴリズムはインデントされた出力で印刷できません。ディレクトリとファイルがめちゃくちゃです。解決策はありますか?
ウェイ

12

単純な再帰を使って自分で書いてください:

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
お願いします!呼び出し側がファイルリストを初期化できるようにして、毎回その無効性をチェックする必要がないようにします。リストを作成する2番目の(パブリック)メソッドを作成する場合は、この内部メソッドを呼び出して完全なリストを返します。
helios

1
なんでも。nullチェックはそれほど高価ではありませんが、利便性と個人的な好みは別として、彼はポイントを獲得すると思います。
pstanton、2013年

もう少し冗長に説明できますか?
2013年

8

私はこれでうまくいくと思います:

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

このようにして、ファイルとディレクトリがあります。ここで再帰を使用し、dirsに対して同じことを行います(FileクラスにはisDirectory()メソッドがあります)。


8

Java 7では、次のクラスを使用できます。

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では、Filesユーティリティを使用してファイルツリーをウォークできるようになりました。とても簡単です。

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

7

このコードは実行する準備ができています

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

再帰的なトラバーサル以外に、ビジターベースのアプローチを使用することもできます。

以下のコードは、トラバーサルにビジターベースのアプローチを使用しています。プログラムへの入力は、トラバースするルートディレクトリであることが想定されています。

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

以下のコードを使用して、特定のフォルダまたはディレクトリのファイルのリストを再帰的に取得できます。

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

受け入れ答えあなたがラムダの内部でIOをしたいとき、それが故障しかし、素晴らしいです。

アクションがIOExceptionsを宣言した場合にできることは、次のとおりです。

フィルタリングされたストリームをとして扱い、Iterable通常のfor-eachループでアクションを実行できます。このように、ラムダ内で例外を処理する必要はありません。

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);
    }
}

ここでそのトリックを見つけました:https : //stackoverflow.com/a/32668807/1207791


1

単一のリストを持つ非再帰的なBFS(特定の例は* .emlファイルの検索です):

    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

私のバージョン(もちろん、Java 8の組み込みウォークを使用することもできました;-)):

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

ここで使用するシンプルだが完全に機能するソリューション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

私はすべてのファイル/ファイル名を再帰的に印刷するためにこれを思いつきました。

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

例では、java.nioのFiles.find()を使用して、*。csvファイルをディレクトリ再帰検索サブディレクトリに出力します。

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);
        }

    }

Stream-resultでforeachを使用して、ブライアンによって与えられた#1の例でファイル名パラメーターを渡す方法を理解するのに問題があったので、この例を投稿します-

お役に立てれば。


0

KotlinはFileTreeWalkこの目的のために持っています。例えば:

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

指定されたルートの下にあるすべての非ディレクトリファイルのテキストリストを作成します。1行に1つのファイルがあり、ルートと長さの相対パスが指定されています。


0

誰かがすでにJava 8ウォークを提供している場合でも、別の方法を実行できます。

これはすべてのファイルを再帰的に提供します

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

-1

スタッカーの回答に基づく。これは、外部ライブラリなしでJSPで機能するソリューションであり、サーバーのほぼどこにでも配置できます。

<!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() + "/";
%>

次に、次のようにします。

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

1
おそらく機能しますが、問題はファイルの閲覧についてであり、閲覧したファイルのレンダリングではありません。そのようにアルゴリズムを公開することをお勧めします。JSP内にビジネスロジックを埋め込むことはお勧めできません。
Samuel Kerrien 2015年

それはあなたが何をしているかに依存します。企業規模のアプリケーションでは、あなたは絶対的に正しいです。シンプルなスタンドアロンのリストへのドロップインとしてこれが必要な場合は、これで十分です。
Nux
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.