Mam tę funkcję, która rekurencyjnie wypisuje nazwy wszystkich plików w katalogu. Problem polega na tym, że mój kod jest bardzo wolny, ponieważ musi uzyskiwać dostęp do zdalnego urządzenia sieciowego w każdej iteracji.
Mój plan polega na tym, aby najpierw załadować wszystkie pliki z katalogu rekurencyjnie, a następnie przejrzeć wszystkie pliki za pomocą wyrażenia regularnego, aby odfiltrować wszystkie pliki, których nie chcę. Czy ktoś ma lepszą sugestię?
public static printFnames(String sDir){
File[] faFiles = new File(sDir).listFiles();
for(File file: faFiles){
if(file.getName().matches("^(.*?)")){
System.out.println(file.getAbsolutePath());
}
if(file.isDirectory()){
printFnames(file.getAbsolutePath());
}
}
}
To tylko test później. Nie zamierzam używać takiego kodu, zamiast tego dodam ścieżkę i datę modyfikacji każdego pliku, który pasuje do zaawansowanego wyrażenia regularnego do tablicy.
Odpowiedzi:
Zakładając, że jest to rzeczywisty kod produkcyjny będziesz pisać, to proponuję przy użyciu rozwiązanie do tego typu rzeczy, które zostały już rozwiązane - Apache Commons IO , a konkretnie
FileUtils.listFiles()
. Obsługuje zagnieżdżone katalogi, filtry (na podstawie nazwy, czasu modyfikacji itp.).Na przykład dla Twojego wyrażenia regularnego:
Collection files = FileUtils.listFiles( dir, new RegexFileFilter("^(.*?)"), DirectoryFileFilter.DIRECTORY );
Spowoduje to rekurencyjne wyszukiwanie plików pasujących do
^(.*?)
wyrażenia regularnego, zwracając wyniki jako kolekcję.Warto zauważyć, że nie będzie to szybsze niż toczenie własnego kodu, robi to samo - przeszukiwanie systemu plików w Javie jest po prostu powolne. Różnica polega na tym, że wersja Apache Commons nie będzie zawierała żadnych błędów.
źródło
Files.walk(Paths.get("/etc")).filter(Files::isRegularFile).collect(Collectors.toList())
W Java 8, to jest 1-liniowej przez
Files.find()
z dowolnie dużą głębokość (na przykład999
), aBasicFileAttributes
zisRegularFile()
public static printFnames(String sDir) { Files.find(Paths.get(sDir), 999, (p, bfa) -> bfa.isRegularFile()).forEach(System.out::println); }
Aby dodać więcej filtrowania, popraw lambdę, na przykład wszystkie pliki jpg zmodyfikowane w ciągu ostatnich 24 godzin:
(p, bfa) -> bfa.isRegularFile() && p.getFileName().toString().matches(".*\\.jpg") && bfa.lastModifiedTime().toMillis() > System.currentMillis() - 86400000
źródło
Jest to bardzo prosta rekurencyjna metoda pobierania wszystkich plików z podanego katalogu głównego.
Wykorzystuje klasę Java 7 NIO Path.
private List<String> getFileNames(List<String> fileNames, Path dir) { try(DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) { for (Path path : stream) { if(path.toFile().isDirectory()) { getFileNames(fileNames, path); } else { fileNames.add(path.toAbsolutePath().toString()); System.out.println(path.getFileName()); } } } catch(IOException e) { e.printStackTrace(); } return fileNames; }
źródło
Wraz z Javą 7 wprowadzono szybszy sposób poruszania się po drzewie katalogów z funkcjonalnością
Paths
iFiles
. Są znacznie szybsze niż „stary”File
sposób.Byłby to kod do przechodzenia i sprawdzania nazw ścieżek za pomocą wyrażenia regularnego:
public final void test() throws IOException, InterruptedException { final Path rootDir = Paths.get("path to your directory where the walk starts"); // Walk thru mainDir directory Files.walkFileTree(rootDir, new FileVisitor<Path>() { // First (minor) speed up. Compile regular expression pattern only one time. private Pattern pattern = Pattern.compile("^(.*?)"); @Override public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes atts) throws IOException { boolean matches = pattern.matcher(path.toString()).matches(); // TODO: Put here your business logic when matches equals true/false return (matches)? FileVisitResult.CONTINUE:FileVisitResult.SKIP_SUBTREE; } @Override public FileVisitResult visitFile(Path path, BasicFileAttributes mainAtts) throws IOException { boolean matches = pattern.matcher(path.toString()).matches(); // TODO: Put here your business logic when matches equals true/false return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path path, IOException exc) throws IOException { // TODO Auto-generated method stub return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path path, IOException exc) throws IOException { exc.printStackTrace(); // If the root directory has failed it makes no sense to continue return path.equals(rootDir)? FileVisitResult.TERMINATE:FileVisitResult.CONTINUE; } }); }
źródło
Szybki sposób na pobranie zawartości katalogu za pomocą Java 7 NIO:
import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.FileSystems; import java.nio.file.Path; ... Path dir = FileSystems.getDefault().getPath( filePath ); DirectoryStream<Path> stream = Files.newDirectoryStream( dir ); for (Path path : stream) { System.out.println( path.getFileName() ); } stream.close();
źródło
Files.newDirectoryStream
może zgłosić IOException. Sugeruję zawinięcie tego wiersza w instrukcję try-with-with Java7, aby strumień był zawsze zamknięty dla Ciebie (wyjątek lub nie, bez potrzeby afinally
). Zobacz także tutaj: stackoverflow.com/questions/17739362/ ...Interfejs Java do odczytywania zawartości folderów systemu plików nie jest zbyt wydajny (jak odkryłeś). JDK 7 rozwiązuje ten problem dzięki zupełnie nowemu interfejsowi do tego typu rzeczy, który powinien zapewnić wydajność na poziomie natywnym w tego rodzaju operacjach.
Podstawowym problemem jest to, że Java tworzy natywne wywołanie systemowe dla każdego pojedynczego pliku. W przypadku interfejsu o niskim opóźnieniu nie jest to aż tak wielka sprawa - ale w sieci z nawet umiarkowanym opóźnieniem naprawdę się sumuje. Jeśli sprofilujesz swój algorytm powyżej, przekonasz się, że większość czasu spędzasz na nieznośnym wywołaniu isDirectory () - to dlatego, że ponosisz podróż w obie strony dla każdego pojedynczego wywołania isDirectory (). Większość nowoczesnych systemów operacyjnych może dostarczać tego rodzaju informacji, gdy pierwotnie zażądano listy plików / folderów (w przeciwieństwie do odpytywania każdej ścieżki pliku o jej właściwości).
Jeśli nie możesz czekać na JDK7, jedną ze strategii rozwiązania tego opóźnienia jest przejście na wiele wątków i użycie usługi ExecutorService z maksymalną liczbą wątków do wykonania rekursji. To nie jest świetne (musisz radzić sobie z blokowaniem struktur danych wyjściowych), ale będzie o wiele szybsze niż wykonanie tego pojedynczego wątku.
We wszystkich dyskusjach na ten temat zdecydowanie zalecam porównanie z najlepszymi, jakie można zrobić, używając kodu natywnego (lub nawet skryptu wiersza poleceń, który robi mniej więcej to samo). Mówienie, że przemierzanie struktury sieci zajmuje godzinę, wcale nie znaczy tak wiele. Poinformowanie nas, że możesz to zrobić natywnie w 7 sekund, ale w Javie zajmuje to godzinę, przyciągnie uwagę ludzi.
źródło
to będzie działać dobrze ... i będzie rekurencyjne
File root = new File("ROOT PATH"); for ( File file : root.listFiles()) { getFilesRecursive(file); } private static void getFilesRecursive(File pFile) { for(File files : pFile.listFiles()) { if(files.isDirectory()) { getFilesRecursive(files); } else { // do your thing // you can either save in HashMap and use it as // per your requirement } } }
źródło
Osobiście podoba mi się ta wersja FileUtils. Oto przykład, który wyszukuje wszystkie pliki mp3 lub flac w katalogu lub dowolnym z jego podkatalogów:
String[] types = {"mp3", "flac"}; Collection<File> files2 = FileUtils.listFiles(/path/to/your/dir, types , true);
źródło
To zadziała dobrze
public void displayAll(File path){ if(path.isFile()){ System.out.println(path.getName()); }else{ System.out.println(path.getName()); File files[] = path.listFiles(); for(File dirOrFile: files){ displayAll(dirOrFile); } } }
źródło
Ta funkcja prawdopodobnie wyświetli całą nazwę pliku i jego ścieżkę z jego katalogu i jego podkatalogów.
public void listFile(String pathname) { File f = new File(pathname); File[] listfiles = f.listFiles(); for (int i = 0; i < listfiles.length; i++) { if (listfiles[i].isDirectory()) { File[] internalFile = listfiles[i].listFiles(); for (int j = 0; j < internalFile.length; j++) { System.out.println(internalFile[j]); if (internalFile[j].isDirectory()) { String name = internalFile[j].getAbsolutePath(); listFile(name); } } } else { System.out.println(listfiles[i]); } } }
źródło
Java 8
public static void main(String[] args) throws IOException { Path start = Paths.get("C:\\data\\"); try (Stream<Path> stream = Files.walk(start, Integer.MAX_VALUE)) { List<String> collect = stream .map(String::valueOf) .sorted() .collect(Collectors.toList()); collect.forEach(System.out::println); } }
źródło
Twoje przeczucie jest złe. Tak działają systemy plików. Nie ma szybszego sposobu (z wyjątkiem sytuacji, gdy musisz to robić wielokrotnie lub dla różnych wzorców, możesz buforować wszystkie ścieżki plików w pamięci, ale wtedy musisz poradzić sobie z unieważnieniem pamięci podręcznej, tj. Co się dzieje, gdy pliki są dodawane / usuwane / zmieniane podczas aplikacja działa).
źródło
Po prostu wiesz, że isDirectory () jest dość powolną metodą. W mojej przeglądarce plików jest to dość powolne. Będę szukał biblioteki, aby zastąpić ją kodem natywnym.
źródło
Bardziej wydajnym sposobem radzenia sobie z milionami folderów i plików jest przechwycenie listy katalogów za pomocą polecenia DOS w jakimś pliku i przeanalizowanie go. Po przeanalizowaniu danych możesz przeprowadzić analizę i obliczyć statystyki.
źródło
import java.io.*; public class MultiFolderReading { public void checkNoOfFiles (String filename) throws IOException { File dir=new File(filename); File files[]=dir.listFiles();//files array stores the list of files for(int i=0;i<files.length;i++) { if(files[i].isFile()) //check whether files[i] is file or directory { System.out.println("File::"+files[i].getName()); System.out.println(); } else if(files[i].isDirectory()) { System.out.println("Directory::"+files[i].getName()); System.out.println(); checkNoOfFiles(files[i].getAbsolutePath()); } } } public static void main(String[] args) throws IOException { MultiFolderReading mf=new MultiFolderReading(); String str="E:\\file"; mf.checkNoOfFiles(str); } }
źródło
W Guava nie musisz czekać na zwrot kolekcji, ale możesz iterować po plikach. Łatwo sobie wyobrazić
IDoSomethingWithThisFile
interfejs w podpisie poniższej funkcji:public static void collectFilesInDir(File dir) { TreeTraverser<File> traverser = Files.fileTreeTraverser(); FluentIterable<File> filesInPostOrder = traverser.preOrderTraversal(dir); for (File f: filesInPostOrder) System.out.printf("File: %s\n", f.getPath()); }
TreeTraverser pozwala również na przełączanie się między różnymi stylami przechodzenia.
źródło
public class GetFilesRecursive { public static List <String> getFilesRecursively(File dir){ List <String> ls = new ArrayList<String>(); for (File fObj : dir.listFiles()) { if(fObj.isDirectory()) { ls.add(String.valueOf(fObj)); ls.addAll(getFilesRecursively(fObj)); } else { ls.add(String.valueOf(fObj)); } } return ls; } public static List <String> getListOfFiles(String fullPathDir) { List <String> ls = new ArrayList<String> (); File f = new File(fullPathDir); if (f.exists()) { if(f.isDirectory()) { ls.add(String.valueOf(f)); ls.addAll(getFilesRecursively(f)); } } else { ls.add(fullPathDir); } return ls; } public static void main(String[] args) { List <String> ls = getListOfFiles("/Users/srinivasab/Documents"); for (String file:ls) { System.out.println(file); } System.out.println(ls.size()); } }
źródło
Kolejny zoptymalizowany kod
import java.io.File; import java.util.ArrayList; import java.util.List; public class GetFilesRecursive { public static List <String> getFilesRecursively(File dir){ List <String> ls = new ArrayList<String>(); if (dir.isDirectory()) for (File fObj : dir.listFiles()) { if(fObj.isDirectory()) { ls.add(String.valueOf(fObj)); ls.addAll(getFilesRecursively(fObj)); } else { ls.add(String.valueOf(fObj)); } } else ls.add(String.valueOf(dir)); return ls; } public static void main(String[] args) { List <String> ls = getFilesRecursively(new File("/Users/srinivasab/Documents")); for (String file:ls) { System.out.println(file); } System.out.println(ls.size()); } }
źródło
Jeszcze jeden przykład wyświetlania plików i katalogów przy użyciu języka Java 8
filter
public static void main(String[] args) { System.out.println("Files!!"); try { Files.walk(Paths.get(".")) .filter(Files::isRegularFile) .filter(c -> c.getFileName().toString().substring(c.getFileName().toString().length()-4).contains(".jpg") || c.getFileName().toString().substring(c.getFileName().toString().length()-5).contains(".jpeg") ) .forEach(System.out::println); } catch (IOException e) { System.out.println("No jpeg or jpg files"); } System.out.println("\nDirectories!!\n"); try { Files.walk(Paths.get(".")) .filter(Files::isDirectory) .forEach(System.out::println); } catch (IOException e) { System.out.println("No Jpeg files"); } }
źródło