- [Java] 특정 디렉토리의 파일 목록 가져오기2023년 11월 04일
- 알쓸개잡
- 작성자
- 2023.11.04.오후09:26
Java API를 사용하여 숨겨진 파일을 포함하여 디렉터리에 있는 모든 파일을 재귀적으로 가져오는 방법에 대해서 알아보자.
- Files.list()
- Files.walk()
- DirectorySystem
- File.listFiles()
- File.listFiles(filter)
- Stream.filter()
테스트를 위한 샘플 디렉토리 구조는 다음과 같다.
샘플 디렉토리 구조 sample-directory의 절대경로는 /java-project/blog/filelist/sample-directory이다.
Directory의 파일만 리스팅
Files.list()를 이용한 파일 목록 스트리밍
하위 디렉터리와 하위 디렉토리 파일을 제외한 해당 디렉터리의 파일 목록만 가져올 때 사용한다.
//samplePath의 Path 타입이고 경로는 /java-project/blog/filelist/sample-directory/ 이다. @Test void files_list_test() throws IOException { try (Stream<Path> pathStream = Files.list(samplePath)) { List<File> list = pathStream .map(Path::toFile) .filter(File::isFile) .toList(); list.forEach(System.out::println); } }
/java-project/blog/filelist/sample-directory/file2.txt /java-project/blog/filelist/sample-directory/file1.txt /java-project/blog/filelist/sample-directory/file4.extension /java-project/blog/filelist/sample-directory/file3.extension
DirectoryStream을 이용한 파일 목록
DirectoryStream은 looping을 사용하여 디렉터리의 항목을 반복하는 데 사용된다.
DirectoryStream을 닫으면 스트림과 관련된 모든 리소스가 해제된다.
스트림을 닫지 않으면 resource leak이 발생할 수 있으므로 try-with-resources 구문을 사용하는 것이 좋다.
@Test void directory_stream_test() { List<File> fileList = new ArrayList<>(); try (DirectoryStream<Path> stream = Files.newDirectoryStream(samplePath)){ stream.forEach(path -> { if (!Files.isDirectory(path)) { fileList.add(path.toFile()); } }); } catch (IOException e) { System.out.println("IOException " + e.getMessage()); } fileList.forEach(System.out::println); }
/java-project/blog/filelist/sample-directory/file2.txt /java-project/blog/filelist/sample-directory/file1.txt /java-project/blog/filelist/sample-directory/file4.extension /java-project/blog/filelist/sample-directory/file3.extension
Directory의 하위 디렉터리를 포함한 파일 리스팅
Files.walk()를 이용한 파일목록 스트리밍
Files.walk() 메서드는 지정된 시작 디렉터리에서 시작하여 디렉토리 depth를 순회한 결과에 대한 stream을 반환한다.
@Test void files_walk_test() { List<Path> pathList = new ArrayList<>(); try (Stream<Path> pathStream = Files.walk(samplePath)) { pathList = pathStream.map(Path::normalize) .filter(Files::isRegularFile) .toList(); } catch (IOException e) { System.out.println("IOException " + e.getMessage()); } pathList.forEach(System.out::println); }
/java-project/blog/filelist/sample-directory/file2.txt /java-project/blog/filelist/sample-directory/file1.txt /java-project/blog/filelist/sample-directory/directory2/directory2-2/directory2-2-file1 /java-project/blog/filelist/sample-directory/directory2/directory2-1/directory2-1-file1 /java-project/blog/filelist/sample-directory/file4.extension /java-project/blog/filelist/sample-directory/file3.extension /java-project/blog/filelist/sample-directory/directory1/directory1-2/directory1-2-file1 /java-project/blog/filelist/sample-directory/directory1/directory1-1/directory1-1-file1
하위 디렉터리의 파일을 포함한 목록을 출력하였다.
하위 디렉터리를 포함하려면 filter(Files::isRegularFile)을 제거하면 된다.
파일 목록이 아닌 디렉토리 목록만 출력하기 위해서는 filter(Files::isDirectory)로 수정하면 된다.
@Test void files_walk_test() { List<Path> pathList = new ArrayList<>(); try (Stream<Path> pathStream = Files.walk(samplePath)) { pathList = pathStream.map(Path::normalize) .filter(Files::isDirectory) .toList(); } catch (IOException e) { System.out.println("IOException " + e.getMessage()); } pathList.forEach(System.out::println); }
/java-project/blog/filelist/sample-directory /java-project/blog/filelist/sample-directory/directory2 /java-project/blog/filelist/sample-directory/directory2/directory2-2 /java-project/blog/filelist/sample-directory/directory2/directory2-1 /java-project/blog/filelist/sample-directory/directory1 /java-project/blog/filelist/sample-directory/directory1/directory1-2 /java-project/blog/filelist/sample-directory/directory1/directory1-1
Recursion을 사용한 파일 목록
File.isDirectory()와 File.listFiles()를 이용하여 재귀적으로 파일 목록을 가져올 수 있다.
@Test void recursion_test() { List<File> files = listFiles(samplePath.toString()); files.forEach(System.out::println); } private List<File> listFiles(String path) { List<File> fileList = new ArrayList<>(); File[] files = new File(path).listFiles(); for (File file : files) { if (file.isDirectory()) { fileList.addAll(listFiles(file.getPath())); } else { fileList.add(file); } } return fileList; }
/java-project/blog/filelist/sample-directory/file2.txt /java-project/blog/filelist/sample-directory/file1.txt /java-project/blog/filelist/sample-directory/directory2/directory2-2/directory2-2-file1 /java-project/blog/filelist/sample-directory/directory2/directory2-1/directory2-1-file1 /java-project/blog/filelist/sample-directory/file4.extension /java-project/blog/filelist/sample-directory/file3.extension /java-project/blog/filelist/sample-directory/directory1/directory1-2/directory1-2-file1 /java-project/blog/filelist/sample-directory/directory1/directory1-1/directory1-1-file1
File 객체의 listFiles() 메서드의 경우 FileFilter 혹은 FilenameFilter를 인자로 전달하여 특정 조건의 파일 목록을 가져올 수 있다.
FileFilter는 Predicate 유형의 함수형 인터페이스로써 다음과 같다.
@FunctionalInterface public interface FileFilter { /** * Tests whether or not the specified abstract pathname should be * included in a pathname list. * * @param pathname The abstract pathname to be tested * @return {@code true} if and only if {@code pathname} * should be included */ boolean accept(File pathname); }
FilenameFilter 도 지원하는데 역시 BiPredicate 유형의 함수형 인터페이스로써 다음과 같다.
@FunctionalInterface public interface FilenameFilter { /** * Tests if a specified file should be included in a file list. * * @param dir the directory in which the file was found. * @param name the name of the file. * @return {@code true} if and only if the name should be * included in the file list; {@code false} otherwise. */ boolean accept(File dir, String name); }
FileFilter와 FilenameFilter는 람다 표현식으로 정의할 수 있는데 람다 표현식에 대해서는 다음 링크를 참고하기 바란다.
2023.09.15 - [자바] - Java - 람다 표현식(lambda expression) - 4개의 주요 functional interface
2023.09.10 - [자바] - Java - 람다 표현식 (lambda expression) 개요
FileFilter와 FilenameFilter를 사용하여 특정 파일만 가져오거나 특정 확장자를 가진 파일만 가져오는 테스트 코드다.
@Test void recursion_test() { //파일 목록에서 확장자가 .txt 인 파일 목록만 추출 FilenameFilter filenameFilter = (file, name) -> name.endsWith(".txt"); //FileFilter를 대체하여 사용 가능하다. //물론 listFiles의 2번째 파라미터 타입은 FileFilter로 변경해야 한다. //FileFilter filter = file -> file.getName().endsWith(".txt"); List<File> files = listFiles(samplePath.toString(), filenameFilter); files.forEach(System.out::println); } private List<File> listFiles(String path, FilenameFilter filter) { List<File> fileList = new ArrayList<>(); File[] files = new File(path).listFiles(filter); for (File file : files) { if (file.isDirectory()) { fileList.addAll(listFiles(file.getPath(), filter)); } else { fileList.add(file); } } return fileList; }
/java-project/blog/filelist/sample-directory/file2.txt /java-project/blog/filelist/sample-directory/file1.txt
*** 특정 확장자의 파일 목록을 가져오는 것은 Stream 방식을 사용하는 경우에도 filter를 통해서 다음과 같이 사용 가능하다.
@Test void files_walk_extension_test() { List<Path> pathList = new ArrayList<>(); try (Stream<Path> pathStream = Files.walk(samplePath)) { pathList = pathStream.map(Path::normalize) .filter(Files::isRegularFile) .filter(file -> file.getFileName().toString().endsWith(".extension")) .toList(); } catch (IOException e) { System.out.println("IOException " + e.getMessage()); } pathList.forEach(System.out::println); }
/java-project/blog/filelist/sample-directory/file4.extension /java-project/blog/filelist/sample-directory/file3.extension
'자바' 카테고리의 다른 글
java21 - scoped value에 대해서 알아보자 (0) 2023.11.26 java 21 처리량 향상을 위한 대안 - virtual thread 알아보자 (0) 2023.11.24 java collection sort using Comparator (0) 2023.10.17 java generic class (0) 2023.09.24 java time convert (시간 변환) (0) 2023.09.17 다음글이전글이전 글이 없습니다.댓글