Java에서 파일을 재귀 적으로 나열


258

Java에서 디렉토리 아래의 모든 파일을 재귀 적으로 나열하려면 어떻게합니까? 프레임 워크가 유틸리티를 제공합니까?

나는 많은 해키 구현을 보았습니다. 그러나 프레임 워크 나 nio 에서 아무도


2
방금 많은 답변에 대한 성능 테스트를 제공하는 테스트 결과 를 완료했습니다 . 당연히 모든 NIO 기반 답변이 가장 잘 수행됩니다. commons-io 답변은 실행 길이가 두 배 이상인 최악의 성능입니다.
Brett Ryan

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
초보자도 기능 프로그래밍의 마법을 보여줄 수있는 예제 중 하나입니다.
Johnny

2
이것의 성능은 Java 8 이전의 메소드와 어떻게 비교됩니까? 현재 디렉토리 탐색이 너무 느리고 속도가 빨라지는 것을 찾고 있습니다.
Sridhar Sarnobat

1
제공된 답변에 대부분의 변형이 포함 된 테스트를 작성 중입니다. 지금까지 Files.walk병렬 스트림과 함께 사용 하는 것이 가장 좋으며 그 뒤에는 Files.walkFileTree약간 느립니다. commons-io를 사용하여 허용되는 답변은 테스트에서 4 배 느리게 느립니다.
Brett Ryan

1
@BrettRyan, 솔루션을 시도했지만 예외가 발생 Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException합니다. 내가 어떻게 고칠 수 있을까
Kachna

5
이것으로부터 실제 파일 목록을 어떻게 얻습니까?
thouliha

159

Fileutils의는iterateFileslistFiles방법. 그들에게 시도하십시오. ( commons-io에서 )

편집 : 당신은 할 수 있습니다 여기 에서 다양한 접근 방식의 벤치 마크를 확인할 . commons-io 접근 방식이 느리므로 여기 에서 더 빠른 것을 선택 하십시오 (중요한 경우)


40
참고 / TLDR : 필터링없이 모든 파일을 재귀 적으로 나열하려면 do를 수행하십시오 FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE). 여기서는 dir기본 디렉토리를 가리키는 File 객체입니다.
andronikus

2
빈 폴더를 반환하지 않으므로을 사용 listFilesAndDirs()하는 listFiles()것이 좋습니다.
schnatterer 2013

1
@MikeFHay FileUtils 코드를 보면 그럴 수 있다고 생각합니다 FileUtils.listFiles(dir, true, true). 를 사용 FileUtils.listFiles(dir, null, true)하면 예외가 발생하지만 FileUtils.listFiles(dir, true, null)하위 디렉토리를 보지 않고 모든 파일을 나열합니다.
ocramot

JDK 네이티브 라이브러리는 어떻습니까? 나는 이것을 쉽게 구현할 수 있지만 다른 곳에서 C & P가 될 것이다
Christian Bongiorno

1
몇 가지 테스트를 통합하고 있지만 지금까지는 JDK8 또는 JDK7 대안을 사용하는 것보다 4 배 느리게 수행되는 것 같습니다. 또한 심볼릭 링크는 특히 트리의 상위 디렉토리에 링크되는 경우이 방법에 문제가있는 것으로 판명되어 필터를 처리하여 피할 수는 있지만 안타깝게도 심볼릭 링크 자체는 방문하지 않습니다. 파일.
Brett Ryan

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
경로 계층 구조에서 더 높은 경로를 가리키는 기호 링크의 경우 메소드가 종료되지 않도록주의하십시오. 을 가리키는 심볼릭 링크가있는 경로를 고려하십시오 -> ..
Brett Ryan

2
이것은 본질적으로 Files.walkFileTree의 나쁜 구현입니다. 사람들이 직접 롤링하는 대신 FIles.walkFileTree를 살펴 보는 것이 좋습니다 ... @BrettRyan이 지적한 정확한 문제를 처리했습니다.
Tyler Nichols

import java.io.File;을 포함 해 주셔서 감사합니다. 너무 많은 예제는 네임 스페이스 또는 데이터 유형을 포함하는 것을 잊어 버려 예를 탐색의 출발점으로 만듭니다. 이 예제는 바로 실행할 수 있습니다. 감사.
barrypicker

Filewalker 파일의 위치에 따라 경로가 다를 수 있습니다. 사용 "/", "./"또는 "../"각각의 루트 디렉토리, 현재 작업 디렉토리 및 상위 디렉토리에 대한
모세 Kirathe

67

자바 7 것은Files.walkFileTree를 :

시작점과 파일 방문자를 제공하면 파일 트리에서 파일을 탐색 할 때 파일 방문자에 대해 다양한 메소드를 호출합니다. 사람들이 재귀 복사, 재귀 이동, 재귀 삭제 또는 재귀 작업을 개발하는 경우 각 파일에 대해 권한을 설정하거나 다른 작업을 수행하는 경우이를 사용할 것을 기대합니다.

이 질문에 대한 전체 Oracle 튜토리얼이 있습니다.


그리고 그것은 걷기의 끝을 알리지 않습니다.
Farid

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 = 새로운 HashSet <파일> (); for (파일 입력 : dir.listFiles ()) {if (entry.isFile ()) {fileTree.add (entry); } else {fileTree.addAll (listFileTree (entry)); }} return fileTree; }
Ben

나에게 이것은 재귀 적 인 가장 간결한 답변입니다.
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);
    }
  }
}

그러나 알고리즘은 들여 쓰기 출력으로 인쇄 할 수 없습니다. 디렉토리와 파일이 엉망입니다. 어떤 해결책?
Wei

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
부디! 호출자가 파일 목록을 초기화하여 매번 null을 검사하지 않아도되도록하십시오. 목록을 작성하는 두 번째 (공용) 메소드를 작성하려면이 내부 메소드를 호출하고 전체 목록을 리턴하십시오.
helios

1
도대체 무엇이. null 확인은 매우 비싸지 않으며 편리함 + 개인적 취향을 제외하고는 그가 요점을 얻을 것이라고 생각합니다.
pstanton

좀 더 자세하게 설명해 주시겠습니까?
uday

8

나는 이것이 일을해야한다고 생각한다.

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

이렇게하면 파일과 디렉토리가 있습니다. 이제 재귀를 사용하고 dirs에 대해서도 동일하게 수행하십시오 ( Fileclass has isDirectory()method).


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.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를 수행 할 때 고장 그러나, 중대하다.

조치가 IOException을 선언하는 경우 수행 할 수있는 작업은 다음과 같습니다.

필터링 된 스트림을로 취급 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 : //.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);
        }

    }

이 예제를 게시하면 Bryan이 제공 한 # 1 예제에서 filename 매개 변수를 전달하는 방법을 이해하는 데 어려움이 있었으므로 foreach on stream-result-

도움이 되었기를 바랍니다.


0

코 틀린은 FileTreeWalk이러한 목적을 가지고 있습니다. 예를 들면 다음과 같습니다.

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

주어진 루트 아래의 모든 비 디렉토리 파일의 텍스트 목록을 생성합니다. 루트와 길이에 상대적인 경로를 가진 행당 한 파일입니다.


0

누군가 이미 Java 8 Walk를 제공하는 경우에도 수행 할 수있는 다른 방법입니다.

이것은 모든 파일을 재귀 적으로 제공합니다.

  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

그것은 당신이하는 일에 달려 있습니다. 엔터프라이즈 규모의 응용 프로그램에서는 절대적으로 옳습니다. 단순한 독립 실행 형 리스팅으로의 드롭 인으로 이것을 필요로한다면 완벽하게 좋습니다.
Nux
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.