Jak rekurencyjnie wyświetlać wszystkie pliki w katalogu w Javie? Czy środowisko udostępnia jakieś narzędzie?
Widziałem wiele nieuczciwych implementacji. Ale żaden z frameworka lub nio
Jak rekurencyjnie wyświetlać wszystkie pliki w katalogu w Javie? Czy środowisko udostępnia jakieś narzędzie?
Widziałem wiele nieuczciwych implementacji. Ale żaden z frameworka lub nio
Odpowiedzi:
Java 8 zapewnia przyjemny strumień do przetwarzania wszystkich plików w drzewie.
Files.walk(Paths.get(path))
.filter(Files::isRegularFile)
.forEach(System.out::println);
Zapewnia to naturalny sposób przeglądania plików. Ponieważ jest to strumień, możesz wykonywać wszystkie fajne operacje na strumieniu, takie jak limit, grupowanie, mapowanie, wcześniejsze wyjście itp.
AKTUALIZACJA : Mogę wskazać, że istnieje także plik Files.find, który pobiera BiPredicate, który może być bardziej wydajny, jeśli chcesz sprawdzić atrybuty pliku.
Files.find(Paths.get(path),
Integer.MAX_VALUE,
(filePath, fileAttr) -> fileAttr.isRegularFile())
.forEach(System.out::println);
Zauważ, że chociaż JavaDoc wymyka się temu, że ta metoda może być bardziej wydajna niż Files.walk , jest ona faktycznie identyczna, różnicę w wydajności można zaobserwować, jeśli również pobierasz atrybuty plików w swoim filtrze. Na koniec, jeśli chcesz filtrować według atrybutów, użyj Files.find , w przeciwnym razie użyj Files.walk , głównie dlatego, że występują przeciążenia i jest to wygodniejsze.
TESTY : Zgodnie z życzeniem przedstawiłem porównanie wydajności wielu odpowiedzi. Sprawdź projekt Github, który zawiera wyniki i przypadek testowy .
Files.walk
strumienia równoległego, a tuż za nim Files.walkFileTree
- tylko nieznacznie wolniej. Akceptowana odpowiedź przy użyciu commons-io jest zdecydowanie najwolniejsza w moich testach i jest 4 razy wolniejsza.
Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException
. Jak mogę to naprawić
FileUtils mają iterateFiles
i listFiles
metody. Wypróbuj je. (z commons-io )
Edycja: tutaj możesz sprawdzić wyniki różnych podejść. Wydaje się, że podejście commons-IO jest powolny, więc wybrać tylko niektóre z nich szybciej stąd (jeśli ma to znaczenie)
FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)
, gdzie dir
jest obiekt File wskazujący katalog podstawowy.
listFilesAndDirs()
, ponieważ listFiles()
nie zwraca pustych folderów.
FileUtils.listFiles(dir, true, true)
. użycie FileUtils.listFiles(dir, null, true)
spowoduje zgłoszenie wyjątku, a FileUtils.listFiles(dir, true, null)
wyświetli listę wszystkich plików bez zaglądania do podkatalogów.
// Gotowy do biegu
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:\\" );
}
}
-> .
.
"/"
, "./"
lub "../"
do katalogu, bieżący katalog roboczy i katalogu nadrzędnego, odpowiednio
Java 7 będzie miał ma Files.walkFileTree :
Jeśli podasz punkt początkowy i gościa pliku, będzie on wywoływał różne metody na gościu pliku podczas przeglądania pliku w drzewie plików. Oczekujemy, że ludzie będą tego używać, jeśli opracowują kopię rekurencyjną, ruch rekurencyjny, rekursywne usuwanie lub operację rekurencyjną, która ustawia uprawnienia lub wykonuje inną operację na każdym z plików.
Jest teraz cały samouczek Oracle dotyczący tego pytania .
Nie są potrzebne biblioteki zewnętrzne.
Zwraca kolekcję, dzięki czemu możesz robić, co chcesz z nią po rozmowie.
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;
}
Wybrałbym coś takiego:
public void list(File file) {
System.out.println(file.getName());
File[] children = file.listFiles();
for (File child : children) {
list(child);
}
}
Plik System.out.println jest po to, aby wskazać, że należy coś zrobić z plikiem. nie ma potrzeby rozróżniania plików i katalogów, ponieważ normalny plik będzie po prostu miał zero elementów potomnych.
listFiles()
: „Jeśli ta abstrakcyjna nazwa ścieżki nie oznacza katalogu, wówczas ta metoda zwraca null
.”
Wolę używać kolejki niż rekurencji do tego rodzaju prostej podróży:
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);
}
}
}
po prostu napisz to sam, używając prostej rekurencji:
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;
}
Myślę, że to powinno wystarczyć:
File dir = new File(dirname);
String[] files = dir.list();
W ten sposób masz pliki i katalogi. Teraz użyj rekurencji i zrób to samo dla katalogów ( File
klasa ma isDirectory()
metodę).
W Javie 7 możesz użyć następującej klasy:
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;
}
}
W Javie 8 możemy teraz użyć narzędzia Pliki do przejścia po drzewie plików. Bardzo prosty.
Files.walk(root.toPath())
.filter(path -> !Files.isDirectory(path))
.forEach(path -> System.out.println(path));
Ten kod jest gotowy do uruchomienia
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);
}
}
}
Oprócz przejścia rekurencyjnego można również zastosować podejście oparte na odwiedzających.
Poniższy kod używa podejścia opartego na odwiedzających do przejścia. Oczekuje się, że dane wejściowe do programu to katalog główny do przejścia.
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);
}
}
}
Możesz użyć poniższego kodu, aby rekursywnie uzyskać listę plików określonego folderu lub katalogu.
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());
}
}
}
Odpowiedź akceptowana jest wielka, jednak załamuje, gdy chcesz zrobić IO wewnątrz lambda.
Oto, co możesz zrobić, jeśli Twoje działanie deklaruje wyjątki IO.
Możesz traktować filtrowany strumień jako Iterable
, a następnie wykonywać swoje czynności w regularnej pętli dla każdego. W ten sposób nie musisz radzić sobie z wyjątkami wewnątrz 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);
}
}
Znalazłem tę sztuczkę tutaj: https://stackoverflow.com/a/32668807/1207791
Nierekurencyjny BFS z pojedynczą listą (szczególnym przykładem jest wyszukiwanie plików * .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);
}
}
Moja wersja (oczywiście mogłem skorzystać z wbudowanego przejścia w Javie 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;
}
Oto proste, ale doskonale działające rozwiązanie wykorzystujące 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);
}
}
}
Wymyśliłem to, aby rekursywnie drukować wszystkie pliki / nazwy plików.
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());
}
}
}
Przykładowe dane wyjściowe * .csv w katalogu przeszukiwanie cykliczne podkatalogów za pomocą Files.find () z 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);
}
}
Publikując ten przykład, ponieważ miałem problem ze zrozumieniem, jak przekazać parametr filename w przykładzie nr 1 podanym przez Bryana, używając foreach na Stream-result -
Mam nadzieję że to pomoże.
Kotlin ma FileTreeWalk
do tego celu. Na przykład:
dataDir.walkTopDown().filter { !it.isDirectory }.joinToString("\n") {
"${it.toRelativeString(dataDir)}: ${it.length()}"
}
Utworzy listę tekstową wszystkich plików innych niż katalogi w danym katalogu głównym, po jednym pliku w wierszu ze ścieżką względem katalogu głównego i długością.
Na podstawie odpowiedzi układacza. Oto rozwiązanie działające w JSP bez żadnych zewnętrznych bibliotek, dzięki czemu możesz umieścić je prawie w dowolnym miejscu na serwerze:
<!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() + "/";
%>
Następnie po prostu robisz coś takiego:
<ul>
<% for (String file : files) { %>
<% if (file.matches(".+\\.(apk|ipa|mobileprovision)")) { %>
<li><%=file.replace(prefixPath, "")%></li>
<% } %>
<% } %>
</ul>