Bagaimana cara mendaftarkan semua file secara rekursif di bawah direktori di Java? Apakah kerangka kerja menyediakan utilitas?
Saya melihat banyak implementasi hacky. Tapi tidak ada dari framework atau nio
Bagaimana cara mendaftarkan semua file secara rekursif di bawah direktori di Java? Apakah kerangka kerja menyediakan utilitas?
Saya melihat banyak implementasi hacky. Tapi tidak ada dari framework atau nio
Jawaban:
Java 8 menyediakan aliran yang bagus untuk memproses semua file dalam pohon.
Files.walk(Paths.get(path))
.filter(Files::isRegularFile)
.forEach(System.out::println);
Ini memberikan cara alami untuk melintasi file. Karena ini adalah streaming, Anda dapat melakukan semua operasi streaming yang bagus pada hasil seperti batas, pengelompokan, pemetaan, keluar lebih awal, dll.
UPDATE : Saya mungkin menunjukkan ada juga Files.find yang mengambil BiPredicate yang bisa lebih efisien jika Anda perlu memeriksa atribut file.
Files.find(Paths.get(path),
Integer.MAX_VALUE,
(filePath, fileAttr) -> fileAttr.isRegularFile())
.forEach(System.out::println);
Perhatikan bahwa sementara JavaDoc menghindari bahwa metode ini bisa lebih efisien daripada Files.walk secara efektif identik, perbedaan dalam kinerja dapat diamati jika Anda juga mengambil atribut file dalam filter Anda. Pada akhirnya, jika Anda perlu memfilter pada atribut menggunakan Files.find , sebaliknya gunakan Files.walk , sebagian besar karena ada kelebihan dan lebih nyaman.
UJI : Seperti yang diminta, saya telah memberikan perbandingan kinerja dari banyak jawaban. Lihat proyek Github yang berisi hasil dan uji kasus .
Files.walk
dengan aliran paralel adalah yang terbaik, diikuti oleh Files.walkFileTree
yang hanya sedikit lebih lambat. Jawaban yang diterima menggunakan commons-io sejauh ini paling lambat oleh tes saya menjadi 4 kali lebih lambat.
Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException
. Bagaimana saya bisa memperbaikinya
FileUtils punya iterateFiles
dan listFiles
metode. Cobalah mereka. (dari commons-io )
Sunting: Anda dapat memeriksa di sini untuk benchmark dari berbagai pendekatan. Tampaknya pendekatan commons-io lambat, jadi pilih yang lebih cepat dari sini (jika itu penting)
FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)
, di mana dir
objek File yang menunjuk ke direktori dasar.
listFilesAndDirs()
, karena listFiles()
tidak mengembalikan folder kosong.
FileUtils.listFiles(dir, true, true)
. menggunakan FileUtils.listFiles(dir, null, true)
akan membuang Pengecualian, sementara FileUtils.listFiles(dir, true, null)
akan daftar semua file tanpa melihat ke subdirektori.
// Siap untuk berlari
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:\\" );
}
}
-> .
.
"/"
, "./"
atau "../"
untuk direktori root, direktori kerja saat ini, dan direktori induk, masing
Java 7 akan memiliki Files.walkFileTree :
Jika Anda memberikan titik awal dan pengunjung file, itu akan memanggil berbagai metode pada pengunjung file saat berjalan melalui file di pohon file. Kami berharap orang untuk menggunakan ini jika mereka mengembangkan salinan rekursif, gerakan rekursif, penghapusan rekursif, atau operasi rekursif yang menetapkan izin atau melakukan operasi lain pada masing-masing file.
Sekarang ada seluruh tutorial Oracle untuk pertanyaan ini .
Tidak diperlukan perpustakaan eksternal.
Mengembalikan Koleksi sehingga Anda dapat melakukan apa pun yang Anda inginkan dengannya setelah panggilan.
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;
}
Saya akan pergi dengan sesuatu seperti:
public void list(File file) {
System.out.println(file.getName());
File[] children = file.listFiles();
for (File child : children) {
list(child);
}
}
System.out.println ada di sana hanya untuk menunjukkan untuk melakukan sesuatu dengan file tersebut. tidak perlu membedakan antara file dan direktori, karena file normal hanya akan memiliki nol anak.
listFiles()
: "Jika pathname abstrak ini tidak menunjukkan direktori, maka metode ini kembali null
."
Saya lebih suka menggunakan antrian atas rekursi untuk jenis traversi sederhana ini:
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);
}
}
}
cukup tulis sendiri menggunakan rekursi sederhana:
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;
}
Saya pikir ini harus dilakukan:
File dir = new File(dirname);
String[] files = dir.list();
Dengan cara ini Anda memiliki file dan direktori. Sekarang gunakan rekursi dan lakukan hal yang sama untuk dirs ( File
class has isDirectory()
method).
Dengan Java 7 Anda dapat menggunakan kelas berikut:
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;
}
}
Di Java 8, sekarang kita dapat menggunakan utilitas File untuk menjalankan hierarki file. Sangat sederhana.
Files.walk(root.toPath())
.filter(path -> !Files.isDirectory(path))
.forEach(path -> System.out.println(path));
Kode ini siap dijalankan
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);
}
}
}
Terlepas dari traversal rekursif seseorang dapat menggunakan pendekatan berbasis Pengunjung juga.
Kode di bawah ini menggunakan pendekatan berbasis Pengunjung untuk traversal. Diharapkan input ke program adalah direktori root untuk dilalui.
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);
}
}
}
Anda dapat menggunakan kode di bawah ini untuk mendapatkan daftar file folder atau direktori tertentu secara rekursif.
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());
}
}
}
The jawaban yang diterima besar, namun itu rusak ketika Anda ingin melakukan IO dalam lambda.
Inilah yang dapat Anda lakukan jika tindakan Anda menyatakan IOExceptions.
Anda dapat memperlakukan aliran yang difilter sebagai Iterable
, dan kemudian melakukan tindakan Anda secara teratur untuk setiap loop. Dengan cara ini, Anda tidak perlu menangani pengecualian di dalam lambda.
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);
}
}
Menemukan trik itu di sini: https://stackoverflow.com/a/32668807/1207791
BFS non-rekursif dengan satu daftar (contoh khusus mencari file * .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);
}
}
Versi saya (tentu saja saya bisa menggunakan built in walk di 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;
}
Di sini solusi sederhana namun berfungsi sempurna menggunakan 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());
}
}
}
private void fillFilesRecursively(File file, List<File> resultFiles) {
if (file.isFile()) {
resultFiles.add(file);
} else {
for (File child : file.listFiles()) {
fillFilesRecursively(child, resultFiles);
}
}
}
Saya datang dengan ini untuk mencetak semua file / nama file secara rekursif.
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());
}
}
}
Contoh menampilkan * .csv file dalam direktori Subdirectories pencarian rekursif menggunakan Files.find () dari java.nio:
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);
}
}
Memposting contoh ini, karena saya mengalami kesulitan memahami cara melewati parameter nama file dalam contoh # 1 yang diberikan oleh Bryan, menggunakan foreach pada hasil-Stream -
Semoga ini membantu.
Kotlin memiliki FileTreeWalk
tujuan ini. Sebagai contoh:
dataDir.walkTopDown().filter { !it.isDirectory }.joinToString("\n") {
"${it.toRelativeString(dataDir)}: ${it.length()}"
}
Akan menghasilkan daftar teks semua file non-direktori di bawah root yang diberikan, satu file per baris dengan path relatif ke root dan panjangnya.
Berdasarkan jawaban stacker. Berikut ini adalah solusi yang bekerja di JSP tanpa pustaka eksternal sehingga Anda dapat meletakkannya hampir di mana saja di server Anda:
<!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() + "/";
%>
Maka Anda hanya melakukan sesuatu seperti:
<ul>
<% for (String file : files) { %>
<% if (file.matches(".+\\.(apk|ipa|mobileprovision)")) { %>
<li><%=file.replace(prefixPath, "")%></li>
<% } %>
<% } %>
</ul>