programing

Java를 사용하여 디렉토리의 모든 파일을 재귀 적으로 나열

nasanasas 2020. 10. 6. 08:18
반응형

Java를 사용하여 디렉토리의 모든 파일을 재귀 적으로 나열


디렉토리에있는 모든 파일의 이름을 재귀 적으로 인쇄하는이 함수가 있습니다. 문제는 반복 할 때마다 원격 네트워크 장치에 액세스해야하기 때문에 내 코드가 매우 느리다는 것입니다.

내 계획은 먼저 디렉토리에서 모든 파일을 재귀 적으로로드 한 다음 정규식이있는 모든 파일을 통해 원하지 않는 모든 파일을 필터링하는 것입니다. 누구든지 더 나은 제안이 있습니까?

public static printFnames(String sDir){
  File[] faFiles = new File(sDir).listFiles();
  for(File file: faFiles){
    if(file.getName().matches("^(.*?)")){
      System.out.println(file.getAbsolutePath());
    }
    if(file.isDirectory()){
      printFnames(file.getAbsolutePath());
    }
  }
}

이것은 나중에 테스트 일뿐입니다. 이와 같은 코드를 사용하지 않을 것입니다. 대신 고급 정규식과 일치하는 모든 파일의 경로와 수정 날짜를 배열에 추가 할 것입니다.


이것이 여러분이 작성하게 될 실제 프로덕션 코드라고 가정하고, 이미 해결 된 이런 종류의 솔루션 인 Apache Commons IO , 특히 FileUtils.listFiles(). 중첩 된 디렉토리, 필터 (이름, 수정 시간 등을 기준으로)를 처리합니다.

예를 들어 정규식의 경우 :

Collection files = FileUtils.listFiles(
  dir, 
  new RegexFileFilter("^(.*?)"), 
  DirectoryFileFilter.DIRECTORY
);

그러면 ^(.*?)정규식과 일치하는 파일을 재귀 적으로 검색 하여 결과를 컬렉션으로 반환합니다.

이것이 여러분 자신의 코드를 롤링하는 것보다 빠르지 않을 것이라는 점은 주목할 가치가 있습니다. 그것은 똑같은 일을합니다. 자바에서 파일 시스템을 트롤링하는 것은 느립니다. 차이점은 Apache Commons 버전에는 버그가 없다는 것입니다.


자바 8에서, 비아 1 라이너의 Files.find()임의의 큰 깊이 (예 999)과 BasicFileAttributesisRegularFile()

public static printFnames(String sDir) {
    Files.find(Paths.get(sDir), 999, (p, bfa) -> bfa.isRegularFile()).forEach(System.out::println);
}

필터링을 더 추가하려면 람다 (예 : 지난 24 시간 동안 수정 된 모든 jpg 파일)를 개선합니다.

(p, bfa) -> bfa.isRegularFile()
  && p.getFileName().toString().matches(".*\\.jpg")
  && bfa.lastModifiedTime().toMillis() > System.currentMillis() - 86400000

이것은 주어진 루트에서 모든 파일을 가져 오는 매우 간단한 재귀 방법입니다.

Java 7 NIO Path 클래스를 사용합니다.

private List<String> getFileNames(List<String> fileNames, Path dir) {
    try(DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
        for (Path path : stream) {
            if(path.toFile().isDirectory()) {
                getFileNames(fileNames, path);
            } else {
                fileNames.add(path.toAbsolutePath().toString());
                System.out.println(path.getFileName());
            }
        }
    } catch(IOException e) {
        e.printStackTrace();
    }
    return fileNames;
} 

Java 7에서는 PathsFiles기능 과 함께 디렉토리 트리를 빠르게 탐색하는 방법이 도입되었습니다 . "이전" File방식 보다 훨씬 빠릅니다 .

다음은 정규 표현식으로 경로 이름을 살펴보고 확인하는 코드입니다.

public final void test() throws IOException, InterruptedException {
    final Path rootDir = Paths.get("path to your directory where the walk starts");

    // Walk thru mainDir directory
    Files.walkFileTree(rootDir, new FileVisitor<Path>() {
        // First (minor) speed up. Compile regular expression pattern only one time.
        private Pattern pattern = Pattern.compile("^(.*?)");

        @Override
        public FileVisitResult preVisitDirectory(Path path,
                BasicFileAttributes atts) throws IOException {

            boolean matches = pattern.matcher(path.toString()).matches();

            // TODO: Put here your business logic when matches equals true/false

            return (matches)? FileVisitResult.CONTINUE:FileVisitResult.SKIP_SUBTREE;
        }

        @Override
        public FileVisitResult visitFile(Path path, BasicFileAttributes mainAtts)
                throws IOException {

            boolean matches = pattern.matcher(path.toString()).matches();

            // TODO: Put here your business logic when matches equals true/false

            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path path,
                IOException exc) throws IOException {
            // TODO Auto-generated method stub
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult visitFileFailed(Path path, IOException exc)
                throws IOException {
            exc.printStackTrace();

            // If the root directory has failed it makes no sense to continue
            return path.equals(rootDir)? FileVisitResult.TERMINATE:FileVisitResult.CONTINUE;
        }
    });
}

Java 7 NIO를 사용하여 디렉토리 내용을 가져 오는 빠른 방법 :

import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.FileSystems;
import java.nio.file.Path;

...

Path dir = FileSystems.getDefault().getPath( filePath );
DirectoryStream<Path> stream = Files.newDirectoryStream( dir );
for (Path path : stream) {
   System.out.println( path.getFileName() );
}
stream.close();

파일 시스템 폴더 내용을 읽기위한 Java의 인터페이스는 성능이 좋지 않습니다. JDK 7은 이러한 종류의 작업에 네이티브 수준의 성능을 가져다 줄 완전히 새로운 인터페이스로이를 수정합니다.

핵심 문제는 Java가 모든 단일 파일에 대해 기본 시스템 호출을한다는 것입니다. 대기 시간이 짧은 인터페이스에서는 그렇게 큰 문제는 아니지만 대기 시간이 보통 인 네트워크에서는 실제로 합산됩니다. 위의 알고리즘을 프로파일 링하면 성가신 isDirectory () 호출에 많은 시간이 소요된다는 것을 알 수 있습니다. isDirectory ()에 대한 모든 단일 호출에 대해 왕복이 발생하기 때문입니다. 대부분의 최신 OS는 파일 / 폴더 목록이 원래 요청되었을 때 이러한 종류의 정보를 제공 할 수 있습니다 (각 개별 파일 경로에서 해당 속성을 쿼리하는 것과 반대).

JDK7을 기다릴 수없는 경우이 지연 시간을 해결하기위한 한 가지 전략은 다중 스레드로 이동하고 최대 스레드 수로 ExecutorService를 사용하여 재귀를 수행하는 것입니다. 좋지는 않지만 (출력 데이터 구조의 잠금을 처리해야 함)이 단일 스레드를 수행하는 것보다 훨씬 빠릅니다.

이러한 종류에 대한 모든 논의에서 네이티브 코드 (또는 거의 동일한 작업을 수행하는 명령 줄 스크립트)를 사용하여 수행 할 수있는 최선과 비교하는 것이 좋습니다. 네트워크 구조를 통과하는 데 한 시간이 걸린다고해서 그다지 의미가 없습니다. 7 초 안에 네이티브로 할 수 있지만 자바에서는 한 시간이 걸린다고 말하면 사람들의 관심을 끌 것입니다.


이것은 잘 작동합니다 ... 그리고 재귀

File root = new File("ROOT PATH");
for ( File file : root.listFiles())
{
    getFilesRecursive(file);
}


private static void getFilesRecursive(File pFile)
{
    for(File files : pFile.listFiles())
    {
        if(files.isDirectory())
        {
            getFilesRecursive(files);
        }
        else
        {
            // do your thing 
            // you can either save in HashMap and use it as
            // per your requirement
        }
    }
}

개인적으로이 버전의 FileUtils를 좋아합니다. 다음은 디렉토리 또는 하위 디렉토리에서 모든 mp3 또는 flacs를 찾는 예입니다.

String[] types = {"mp3", "flac"};
Collection<File> files2 = FileUtils.listFiles(/path/to/your/dir, types , true);

이것은 잘 작동합니다

public void displayAll(File path){      
    if(path.isFile()){
        System.out.println(path.getName());
    }else{
        System.out.println(path.getName());         
        File files[] = path.listFiles();
        for(File dirOrFile: files){
            displayAll(dirOrFile);
        }
    }
}


이 함수는 아마도 모든 파일 이름과 디렉토리 및 하위 디렉토리의 경로를 나열합니다.

public void listFile(String pathname) {
    File f = new File(pathname);
    File[] listfiles = f.listFiles();
    for (int i = 0; i < listfiles.length; i++) {
        if (listfiles[i].isDirectory()) {
            File[] internalFile = listfiles[i].listFiles();
            for (int j = 0; j < internalFile.length; j++) {
                System.out.println(internalFile[j]);
                if (internalFile[j].isDirectory()) {
                    String name = internalFile[j].getAbsolutePath();
                    listFile(name);
                }

            }
        } else {
            System.out.println(listfiles[i]);
        }

    }

}

한 번에 모든 것을 가져 오는 대신 파일 시스템에 액세스하고 모든 하위 디렉토리에 대한 내용을 가져 오는 것이 어리석은 것처럼 느껴집니다.

당신의 감정이 잘못되었습니다. 이것이 파일 시스템이 작동하는 방식입니다. 더 빠른 방법은 없습니다. 앱 실행).


isDirectory ()가 상당히 느린 방법이라는 것을 알고 있습니다. 파일 브라우저에서 속도가 상당히 느립니다. 네이티브 코드로 대체 할 라이브러리를 살펴볼 것입니다.


수백만 개의 폴더와 파일을 처리 할 때 내가 찾은 더 효율적인 방법은 일부 파일에서 DOS 명령을 통해 디렉토리 목록을 캡처하고 구문 분석하는 것입니다. 데이터를 파싱하면 분석을 수행하고 통계를 계산할 수 있습니다.


import java.io.*;

public class MultiFolderReading {

public void checkNoOfFiles (String filename) throws IOException {

    File dir=new File(filename);
    File files[]=dir.listFiles();//files array stores the list of files

 for(int i=0;i<files.length;i++)
    {
        if(files[i].isFile()) //check whether files[i] is file or directory
        {
            System.out.println("File::"+files[i].getName());
            System.out.println();

        }
        else if(files[i].isDirectory())
        {
            System.out.println("Directory::"+files[i].getName());
            System.out.println();
            checkNoOfFiles(files[i].getAbsolutePath());
        }
    }
}

public static void main(String[] args) throws IOException {

    MultiFolderReading mf=new MultiFolderReading();
    String str="E:\\file"; 
    mf.checkNoOfFiles(str);
   }
}

Guava에서는 컬렉션이 반환 될 때까지 기다릴 필요가 없지만 실제로 파일을 반복 할 수 있습니다. IDoSomethingWithThisFile아래 함수의 시그니처에서 인터페이스 를 상상하기 쉽습니다 .

public static void collectFilesInDir(File dir) {
    TreeTraverser<File> traverser = Files.fileTreeTraverser();
    FluentIterable<File> filesInPostOrder = traverser.preOrderTraversal(dir);
    for (File f: filesInPostOrder)
        System.out.printf("File: %s\n", f.getPath());
}

TreeTraverser 를 사용하면 다양한 순회 스타일 사이를 이동할 수도 있습니다.


자바 8

public static void main(String[] args) throws IOException {

        Path start = Paths.get("C:\\data\\");
        try (Stream<Path> stream = Files.walk(start, Integer.MAX_VALUE)) {
            List<String> collect = stream
                .map(String::valueOf)
                .sorted()
                .collect(Collectors.toList());

            collect.forEach(System.out::println);
        }


    }

public class GetFilesRecursive {
    public static List <String> getFilesRecursively(File dir){
        List <String> ls = new ArrayList<String>();
        for (File fObj : dir.listFiles()) {
            if(fObj.isDirectory()) {
                ls.add(String.valueOf(fObj));
                ls.addAll(getFilesRecursively(fObj));               
            } else {
                ls.add(String.valueOf(fObj));       
            }
        }

        return ls;
    }
    public static List <String> getListOfFiles(String fullPathDir) {
        List <String> ls = new ArrayList<String> ();
        File f = new File(fullPathDir);
        if (f.exists()) {
            if(f.isDirectory()) {
                ls.add(String.valueOf(f));
                ls.addAll(getFilesRecursively(f));
            }
        } else {
            ls.add(fullPathDir);
        }
        return ls;
    }

    public static void main(String[] args) {
        List <String> ls = getListOfFiles("/Users/srinivasab/Documents");
        for (String file:ls) {
            System.out.println(file);
        }
        System.out.println(ls.size());
    }
}

또 다른 최적화 된 코드

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class GetFilesRecursive {
    public static List <String> getFilesRecursively(File dir){
        List <String> ls = new ArrayList<String>();
        if (dir.isDirectory())
            for (File fObj : dir.listFiles()) {
                if(fObj.isDirectory()) {
                    ls.add(String.valueOf(fObj));
                    ls.addAll(getFilesRecursively(fObj));               
                } else {
                    ls.add(String.valueOf(fObj));       
                }
            }
        else
            ls.add(String.valueOf(dir));

        return ls;
    }

    public static void main(String[] args) {
        List <String> ls = getFilesRecursively(new File("/Users/srinivasab/Documents"));
        for (String file:ls) {
            System.out.println(file);
        }
        System.out.println(ls.size());
    }
}

참고 URL : https://stackoverflow.com/questions/2534632/list-all-files-from-a-directory-recursively-with-java

반응형