Java'da bir dizin altındaki tüm dosyaları özyinelemeli olarak nasıl listeleyebilirim? Çerçeve herhangi bir fayda sağlıyor mu?
Çok çılgın uygulamalar gördüm. Ama çerçeve veya nio'dan hiçbiri
Java'da bir dizin altındaki tüm dosyaları özyinelemeli olarak nasıl listeleyebilirim? Çerçeve herhangi bir fayda sağlıyor mu?
Çok çılgın uygulamalar gördüm. Ama çerçeve veya nio'dan hiçbiri
Yanıtlar:
Java 8, ağaçtaki tüm dosyaları işlemek için güzel bir akış sağlar.
Files.walk(Paths.get(path))
.filter(Files::isRegularFile)
.forEach(System.out::println);
Bu, dosyaları taramak için doğal bir yol sağlar. Bir akış olduğundan, sonuçta limit, gruplama, haritalama, erken çıkış vb.Gibi tüm güzel akış işlemlerini yapabilirsiniz.
GÜNCELLEME : Ben de orada işaret olabilir files.Find bir sürer BiPredicate dosya özelliklerini kontrol etmek için gerekirse daha verimli olabilir.
Files.find(Paths.get(path),
Integer.MAX_VALUE,
(filePath, fileAttr) -> fileAttr.isRegularFile())
.forEach(System.out::println);
JavaDoc, bu yöntemin Files.walk'tan daha verimli olabileceğinden kaçınıyor olsa da, etkili bir şekilde aynıdır, ancak filtrenizdeki dosya özniteliklerini de alıyorsanız, performans farkı gözlemlenebilir. Sonunda, özniteliklere filtre uygulamanız gerekiyorsa Files.find kullanın , aksi takdirde Files.walk'u kullanın , çünkü çoğunlukla aşırı yükler vardır ve daha uygundur.
TESTLER : İstediğim gibi birçok cevabın performans karşılaştırmasını yaptım. Sonuçları ve bir test senaryosunu içeren Github projesine göz atın .
Files.walk
paralel bir akışla kullanmak en iyisi gibi görünüyor , bunu yakından takip Files.walkFileTree
eden marjinal olarak daha yavaş. Commons-io kullanarak kabul edilen cevap, testlerimin 4 kat daha yavaş olmasıyla açık ara en yavaştır.
Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException
. Nasıl düzeltebilirim
FileUtils var iterateFiles
ve listFiles
yöntemler. Bir deneyin. ( commons-io'dan )
Düzenleme: Burada farklı yaklaşımların bir karşılaştırması için kontrol edebilirsiniz . Müşterek yaklaşım yavaş görünüyor, bu yüzden buradan daha hızlı olanlardan bazılarını seçin (önemliyse)
FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)
, dir
yapın, temel dizine işaret eden bir File nesnesi.
listFilesAndDirs()
olarak, listFiles()
boş klasörleri döndürmez.
FileUtils.listFiles(dir, true, true)
. using FileUtils.listFiles(dir, null, true)
bir İstisna fırlatırken, FileUtils.listFiles(dir, true, null)
tüm dosyaları alt dizinlere bakmadan listeler.
// Koşmak için hazır
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:\\" );
}
}
-> .
.
"/"
, "./"
ya "../"
sırasıyla kök dizininde, geçerli çalışma dizini ve üst dizini için
Java 7 olacak sahiptir Files.walkFileTree :
Bir başlangıç noktası ve bir dosya ziyaretçisi sağlarsanız, dosya ziyaretçisinde dosya ağacında dolaşırken çeşitli yöntemleri çağırır. Kullanıcıların, özyinelemeli bir kopya, özyinelemeli bir hareket, özyinelemeli silme veya her bir dosyada izinleri ayarlayan veya başka bir işlem gerçekleştiren özyinelemeli bir işlem geliştirmeleri durumunda bunu kullanmasını bekliyoruz.
Artık bu soruya ilişkin tüm bir Oracle eğitimi var .
Harici kütüphaneye gerek yok.
Aramadan sonra istediğinizi yapabilmeniz için bir Koleksiyon döndürür.
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;
}
Gibi bir şey gitmek istiyorum:
public void list(File file) {
System.out.println(file.getName());
File[] children = file.listFiles();
for (File child : children) {
list(child);
}
}
System.out.println dosyasıyla bir şey yapmayı göstermek için orada. normal bir dosya sıfır çocuğa sahip olacağından dosyalar ve dizinler arasında ayrım yapmaya gerek yoktur.
listFiles()
: “Bu soyut yol adı bir dizini göstermiyorsa, bu yöntem geri döner null
.”
Ben bu tür basit geçiş için özyineleme üzerinde bir kuyruk kullanmayı tercih ederim:
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);
}
}
}
basit özyineleme kullanarak kendiniz yazın:
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;
}
Bunun işi yapması gerektiğini düşünüyorum:
File dir = new File(dirname);
String[] files = dir.list();
Bu şekilde dosya ve dizinleriniz olur. Şimdi özyineleme kullanın ve dirs için aynısını yapın ( File
sınıf isDirectory()
yöntemi vardır).
Java 7 ile aşağıdaki sınıfı kullanabilirsiniz:
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;
}
}
Java 8'de artık bir dosya ağacında yürümek için Dosyalar yardımcı programını kullanabiliriz. Çok basit.
Files.walk(root.toPath())
.filter(path -> !Files.isDirectory(path))
.forEach(path -> System.out.println(path));
Bu kod çalıştırılmaya hazır
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);
}
}
}
Özyinelemeli geçişin dışında Ziyaretçi bazlı bir yaklaşım da kullanılabilir.
Aşağıdaki kod, geçiş için Ziyaretçi tabanlı bir yaklaşım kullanır.Programa girişin geçilecek kök dizin olması beklenir.
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);
}
}
}
Belirli bir klasör veya dizindeki dosyaların listesini özyineli olarak almak için aşağıdaki kodu kullanabilirsiniz.
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());
}
}
}
Kabul edilen cevap harika, ancak lambda içinde IO yapmak istediğinizde yıkılıyor.
Eyleminiz IOExceptions'ı bildirirse neler yapabileceğiniz aşağıda açıklanmıştır.
Filtrelenen akışı bir olarak ele alabilir Iterable
ve ardından eyleminizi her biri için düzenli bir döngüde yapabilirsiniz. Bu şekilde, bir lambda içindeki istisnaları ele almak zorunda kalmazsınız.
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);
}
}
Bu numarayı burada buldum: https://stackoverflow.com/a/32668807/1207791
Tek bir listeyle özyinelemesiz BFS (özel örnek * .eml dosyalarını arıyor):
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);
}
}
Benim sürümüm (tabii ki Java 8 ;-) yerleşik yürüyüş kullanmış olabilir):
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;
}
İşte basit ama mükemmel çalışan bir çözüm 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);
}
}
}
Tüm dosyaları / dosya adlarını tekrar tekrar yazdırmak için bunu buldum.
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());
}
}
}
Örnek, java.nio dosyasından Files.find () kullanarak alt dizinlerde özyinelemeli aramada * .csv dosyaları çıkarır:
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);
}
}
Bu örnekte, Bryan tarafından verilen # 1 örneğinde dosya adı parametresinin nasıl aktarılacağını anladığımda sorun yaşadığım için, Akış sonuçlarında foreach kullanarak -
Bu yardımcı olur umarım.
Kotlin FileTreeWalk
bu amaçla var. Örneğin:
dataDir.walkTopDown().filter { !it.isDirectory }.joinToString("\n") {
"${it.toRelativeString(dataDir)}: ${it.length()}"
}
Belirli bir kök altındaki tüm dizin dışı dosyaların, kök ve uzunluğa göre yolu olan satır başına bir dosya içeren bir metin listesi oluşturur.
İstifleyici cevabına göre. Dış kütüphaneler olmadan JSP'de çalışan bir çözümdür, böylece sunucunuzda neredeyse her yere koyabilirsiniz:
<!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() + "/";
%>
Sonra sadece şöyle bir şey yaparsınız:
<ul>
<% for (String file : files) { %>
<% if (file.matches(".+\\.(apk|ipa|mobileprovision)")) { %>
<li><%=file.replace(prefixPath, "")%></li>
<% } %>
<% } %>
</ul>