programing

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

sourcetip 2022. 12. 7. 00:27
반응형

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

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

내 계획은 먼저 디렉토리의 모든 파일을 재귀적으로 로드한 다음 regex를 사용하여 모든 파일을 검색하여 원하지 않는 모든 파일을 필터링하는 것입니다.더 좋은 제안 있어요?

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

이것은 나중에 실시하는 테스트입니다.이러한 코드를 사용하지 않고, 확장 regex 와 일치하는 모든 파일의 경로와 수정일을 어레이에 추가합니다.

이것이 실제의 실가동 코드의 작성이라고 가정하면, 이미 해결이 끝난, Apache Commons IO의 솔루션을 사용하는 것을 추천합니다.중첩된 디렉토리, 필터(이름, 수정 시간 등에 기반)를 처리합니다.

예를 들어 regex의 경우:

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

으로 이에음음, the the the the the the the에 일치하는 됩니다.^(.*?).regex로 한다.

이것은 자신의 코드를 굴리는 것보다 빠르지 않습니다.Java에서 파일 시스템을 트래핑하는 것은 느립니다.차이점은 Apache Commons 버전에는 버그가 없다는 것입니다.

Java 8 에서는, 1 라인 경유로, 깊이가 임의로 커집니다(예:999) 및 의

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

필터링을 추가하려면 lamda를 강화합니다. 예를 들어 지난 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;
} 

7에서는, 디렉토리 를 보다 할 수 있는 7 에서 도입되었습니다.Paths ★★★★★★★★★★★★★★★★★」Files기능성."구형"보다 훨씬 더 빠릅니다.Filewayway.disclosed.를 클릭합니다.

이 코드는 패스명을 정규 표현으로 체크하기 위한 코드입니다.

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을 기다릴 수 없는 경우 이 지연에 대처하기 위한 한 가지 방법은 멀티 스레드를 사용하여 최대 수의 스레드를 가진 Executor Service를 사용하여 재귀 작업을 수행하는 것입니다.(출력 데이터 구조의 잠금에 대처해야 함) 좋은 것은 아니지만, 이 싱글 스레드보다 훨씬 빠릅니다.

이러한 문제에 대한 모든 논의에서는 네이티브 코드(또는 거의 동일한 작업을 수행하는 명령줄 스크립트)를 사용하여 수행할 수 있는 최선의 방법과 비교할 것을 강력히 권장합니다.네트워크 구조를 통과하는 데 1시간이 걸린다고 해도 그다지 큰 의미는 아닙니다.네이티브로 7초 안에 할 수 있지만 자바에서는 1시간이 걸린다고 하면 사람들의 관심을 끌 수 있습니다.

이것은 잘 작동할 것이다... 그리고 그것의 재귀적

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 또는 flac을 검출하는 예를 다음에 나타냅니다.

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

자바 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 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]);
        }

    }

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

파일 시스템에 접속해 모든 서브디렉토리의 컨텐츠를 취득하는 것은 어리석은 일이라고 생각합니다.

너의 감정은 틀렸다.파일 시스템은 이렇게 작동합니다.더 빠른 방법은 없습니다(반복적으로 또는 다른 패턴의 경우 모든 파일 경로를 메모리에 캐시할 수 있지만, 그런 다음 캐시 무효화(앱 실행 중 파일 추가/삭제/이름 변경)에 대처해야 합니다).

is Directory()는 매우 느린 메서드입니다.파일 브라우저가 느리네요.네이티브 코드로 대체하기 위해 도서관을 찾아볼 거예요.

수백만 개의 폴더와 파일을 처리하는 보다 효율적인 방법은 일부 파일의 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에서는 다양한 트래버설 스타일도 선택할 수 있습니다.

최적화된 다른 코드

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

Java 8을 사용하여 파일 및 디렉토리를 나열하는 다른 예filter

public static void main(String[] args) {

System.out.println("Files!!");
        try {
            Files.walk(Paths.get("."))
                    .filter(Files::isRegularFile)
                    .filter(c ->
                            c.getFileName().toString().substring(c.getFileName().toString().length()-4).contains(".jpg")
                            ||
                            c.getFileName().toString().substring(c.getFileName().toString().length()-5).contains(".jpeg")
                    )
                    .forEach(System.out::println);

        } catch (IOException e) {
        System.out.println("No jpeg or jpg files");
        }

        System.out.println("\nDirectories!!\n");
        try {
            Files.walk(Paths.get("."))
                    .filter(Files::isDirectory)
                    .forEach(System.out::println);

        } catch (IOException e) {
            System.out.println("No Jpeg files");
        }
}

test folder Windows 11에서 60K-files-in-284-folders로 몇 가지 방법을 테스트했습니다.

public class App {

    public static void main(String[] args) throws Exception {
        Path path = Paths.get("E:\\书籍");
        // 1.walkFileTree
        long start1 = System.currentTimeMillis();
        Files.walkFileTree(path, new SimpleFileVisitor<Path>() {

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                // if(pathMatcher.matches(file))
                // files.add(file.toFile());

                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
                // System.out.println(dir.getFileName());
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException e) {
                return FileVisitResult.CONTINUE;
            }

        });
        long end1 = System.currentTimeMillis();
        
        // 2.newDirectoryStream
        long start2 = System.currentTimeMillis();
        search(path.toFile());
        long end2 = System.currentTimeMillis();

        // 3.listFiles
        long start3 = System.currentTimeMillis();
        getFileNames(path);
        long end3 = System.currentTimeMillis();

        System.out.println("\r执行耗时:" + (end1 - start1));
        System.out.println("\r执行耗时:" + (end2 - start2));
        System.out.println("\r执行耗时:" + (end3 - start3));

    }


    private static void getFileNames(Path dir) {
        try(DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
            for (Path path : stream) {
                if(Files.isDirectory(path)) {
                    getFileNames(path);
                }
            }
        } catch(IOException e) {
            e.printStackTrace();
        }
    } 

    public static void search(File file) {
        Queue<File> q = new LinkedList<>();
        q.offer(file);
        while (!q.isEmpty()) {
            try {
                for (File childfile : q.poll().listFiles()) {
                    // System.out.println(childfile.getName());
                    if (childfile.isDirectory()) {
                        q.offer(childfile);
                    }
                }
            } catch (Exception e) {

            }
        }
    }
}

결과(msec):

walk File Tree(워크 파일 트리) listFiles new Directory Stream
68 451 493
64 464 482
61 478 457
67 477 488
59 474 466

알려진 성능 문제:

1.

위의 알고리즘을 프로파일링하면, 대부분의 시간이 귀찮은 isDirectory() 콜에 소비되고 있는 것을 알 수 있습니다.이것은 isDirectory()에의 콜마다 라운드 트립이 발생하기 때문입니다.:: Kevin Day

  1. listfiles()는 모든 엔트리에 대해 새로운 파일개체를 만듭니다.

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

반응형