Java: как получить все вложенные папки рекурсивно?
Прежде чем отлаживать рекурсивную функцию, работающую в конце часа: есть ли команда для получения подкаталогов? giveMeSubDirs(downToPath)
?
// WARNING: RECURSION out of bound or too much data
public HashSet<FileObject> getAllDirs(String path) {
HashSet<FileObject> checkedDirs = new HashSet<FileObject>();
HashSet<FileObject> allDirs = new HashSet<FileObject>();
String startingPath = path;
File fileThing = new File(path);
FileObject fileObject = new FileObject(fileThing);
for (FileObject dir : getDirsInDir(path)) {
// SUBDIR
while ( !checkedDirs.contains(dir)
&& !(getDirsInDir(dir.getFile().getParent()).size() == 0)) {
// DO NOT CHECK TOP DIRS if any bottom dir UNCHECKED!
while ( uncheckedDirsOnLevel(path, checkedDirs).size() > 0) {
while (getDirsInDir(path).size() == 0
|| (numberOfCheckedDirsOnLevel(path, checkedDirs)==getDirsInDir(path).size())) {
allDirs.add(new FileObject(new File(path)));
checkedDirs.add(new FileObject(new File(path)));
if(traverseDownOneLevel(path) == startingPath )
return allDirs;
//get nearer to the root
path = traverseDownOneLevel(path);
}
path = giveAnUncheckedDir(path, checkedDirs);
if ( path == "NoUnchecked.") {
checkedDirs.add(new FileObject( (new File(path)).getParentFile() ));
break;
}
}
}
}
return allDirs;
}
Резюме о коде:
- Идите как можно глубже к дереву каталогов. Когда в режиссере нет режиссера, остановитесь, поставьте его на съемочную площадку, пройдите вверх. Не проверяйте каталоги в наборе.
- Остановитесь и верните набор, если вы достигнете стартового пути.
- Повторите шаги 1 и 2.
ПОМЕЩЕНИЕ: структура каталогов конечна и с небольшим объемом данных.
6 ответов
Вы можете получить все подкаталоги с помощью следующего фрагмента:
File file = new File("path");
File[] subdirs = file.listFiles(new FileFilter() {
public boolean accept(File f) {
return f.isDirectory();
}
});
Это получает только непосредственные подкаталоги, чтобы получить их все рекурсивно, вы можете написать:
List<File> getSubdirs(File file) {
List<File> subdirs = Arrays.asList(file.listFiles(new FileFilter() {
public boolean accept(File f) {
return f.isDirectory();
}
}));
subdirs = new ArrayList<File>(subdirs);
List<File> deepSubdirs = new ArrayList<File>();
for(File subdir : subdirs) {
deepSubdirs.addAll(getSubdirs(subdir));
}
subdirs.addAll(deepSubdirs);
return subdirs;
}
Еще одна версия без рекурсии и в алфавитном порядке. Также использует Set, чтобы избежать циклов (проблема в системах Unix со ссылками).
public static Set<File> subdirs(File d) throws IOException {
TreeSet<File> closed = new TreeSet<File>(new Comparator<File>() {
@Override
public int compare(File f1, File f2) {
return f1.toString().compareTo(f2.toString());
}
});
Deque<File> open = new ArrayDeque<File>();
open.push(d);
closed.add(d);
while ( ! open.isEmpty()) {
d = open.pop();
for (File f : d.listFiles()) {
if (f.isDirectory() && ! closed.contains(f)) {
open.push(f);
closed.add(f);
}
}
}
return closed;
}
Нет, такой функции нет в стандартном API Java. Но есть в Apache commons-io; если вы не хотите включать его в качестве библиотеки, вы также можете посмотреть исходный код.
Пример кода выше отсутствует ");" в конце заявления. Правильный код должен быть:
File file = new File("path");
File[] subdirs = file.listFiles(new FileFilter() {
public boolean accept(File f) {
return f.isDirectory();
}
});
Использование рекурсии:
private void getAllSubFoldersInPath(File path)
{
File[] files=path.listFiles();
try {
for(File file: files)
{
if(file.isDirectory())
{
System.out.println("DIRECTORY:"+file.getCanonicalPath());
getAllSubFoldersInPath(file);
}
else
{
System.out.println("FILE: "+file.getCanonicalPath());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
- Получить все файлы из корневого файла в виде массива (@see listFiles)
- Сортировка только по каталогам, различая файлы и каталоги (@see isDirectory)
- Преобразование (отфильтрованного) массива из шагов 1 и 2 в список
- Добавить все найденные каталоги в результирующий список
- Повторите этот шаблон для каждого файла каталога, который вы нашли на шаге 1, с растущим результирующим списком
- В конце вернуть результирующий список
Все это вложено в некую лямбда-магию:
private static List<File> getAllSubDirectories(File root, List<File> result) {
List<File> currentSubDirs = Arrays.asList(Objects.requireNonNull(root.listFiles(File::isDirectory), "Root file has to be directory"));
result.addAll(currentSubDirs);
currentSubDirs.forEach(file -> getAllSubDirectories(file, result));
return result;
}
Просто начните с корневого файла (который должен быть каталогом) и пустого списка.
Примечание. Шаги 1 и 2 можно комбинировать с фильтром (@see listFiles(фильтр FileFilter))
Это улучшенный код с подходом Java 8. Этот код будет работать на основе рекурсии и находить каталоги до последнего корня.
List<File> findAllSubdirs(File file) {
List<File> subdirs = Arrays.asList(file.listFiles(File::isDirectory));
subdirs = new ArrayList<File>(subdirs);
List<File> deepSubdirs = new ArrayList<File>();
for(File subdir : subdirs) {
deepSubdirs.addAll(findAllSubdirs(subdir));
}
subdirs.addAll(deepSubdirs);
return subdirs;
}
Если вам нужен только немедленный список подкаталогов, попробуйте следующую строку кода.
List<File> subdirs = Arrays.asList(file.listFiles(File::isDirectory));
class DirFileFilter extends FileFilter {
boolean accept(File pathname) {
return pathname.isDirectory();
}
}
DirFileFilter filter = new DirFileFilter();
HashSet<File> files = new HashSet<File>();
void rec(File root) {
// add itself to the list
files.put(root);
File[] subdirs = root.list(filter);
// bound of recursion: must return
if (subdirs.length == 0)
return;
else //this is the recursive case: can call itself
for (File file : subdirs)
rec(file);
}