我有这个函数以递归方式打印目录中所有文件的名称。问题是我的代码非常慢,因为它必须在每次迭代时访问远程网络设备。
我的计划是先递归加载目录中的所有文件,然后使用正则表达式遍历所有文件,过滤掉我不想要的所有文件。有人有更好的建议吗?
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());
}
}
}
这只是后来的一个测试,我不打算使用这样的代码,相反,我将添加与高级正则表达式匹配的每个文件的路径和修改日期。
答案 0 :(得分:127)
假设这是您正在编写的实际生产代码,那么我建议使用解决方案来解决已经解决的问题 - Apache Commons IO,特别是FileUtils.listFiles()
。它处理嵌套目录,过滤器(基于名称,修改时间等)。
例如,对于你的正则表达式:
Collection files = FileUtils.listFiles(
dir,
new RegexFileFilter("^(.*?)"),
DirectoryFileFilter.DIRECTORY
);
这将递归搜索与^(.*?)
正则表达式匹配的文件,并将结果作为集合返回。
值得注意的是,这并不比滚动自己的代码快,它做同样的事情 - 用Java拖曳文件系统只是很慢。不同的是,Apache Commons版本中没有任何错误。
答案 1 :(得分:54)
在Java 8中,它是一个通过Files.find()
的1-liner,具有任意大的深度(例如999
)和BasicFileAttributes
isRegularFile()
public static printFnames(String sDir) {
Files.find(Paths.get(sDir), 999, (p, bfa) -> bfa.isRegularFile()).forEach(System.out::println);
}
要添加更多过滤,请增强lambda,例如过去24小时内修改的所有jpg文件:
(p, bfa) -> bfa.isRegularFile()
&& p.getFileName().toString().matches(".*\\.jpg")
&& bfa.lastModifiedTime().toMillis() > System.currentMillis() - 86400000
答案 2 :(得分:25)
这是一个非常简单的递归方法,可以从给定的根中获取所有文件。
它使用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;
}
答案 3 :(得分:18)
使用Java 7,通过Paths
和Files
功能引入了更快的通过目录树行走的方式。它们比“旧”File
方式快得多。
这将是通过正则表达式遍历并检查路径名的代码:
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;
}
});
}
答案 4 :(得分:13)
使用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();
答案 5 :(得分:12)
Java用于读取文件系统文件夹内容的界面性能不是很高(正如您所发现的那样)。 JDK 7通过一个全新的界面来解决这个问题,它可以为这些操作带来本机级别的性能。
核心问题是Java为每个文件进行本机系统调用。在低延迟接口上,这不是什么大不了的事 - 但在具有中等延迟的网络上,它真的会增加。如果您对上面的算法进行了分析,您会发现大部分时间花在了烦人的isDirectory()调用上 - 这是因为您每次调用isDirectory()都会产生一次往返。大多数现代操作系统可以在最初请求文件/文件夹列表时提供此类信息(而不是查询每个文件路径的属性)。
如果您不能等待JDK7,那么解决此延迟的一种策略是使用多线程并使用具有最多线程数的ExecutorService来执行递归。它不是很好(你必须处理输出数据结构的锁定),但它比这个单线程要快得多。
在关于此类事情的所有讨论中,我强烈建议您使用本机代码(甚至是执行大致相同操作的命令行脚本)进行比较。说遍历网络结构需要一个小时并不是真的意味着那么多。告诉我们你可以在7秒内完成原生,但是用Java需要一个小时才会引起人们的注意。
答案 6 :(得分:5)
这将正常工作......及其递归
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
}
}
}
答案 7 :(得分:3)
我个人喜欢这个版本的FileUtils。这是一个查找目录或其任何子目录中的所有mp3或flacs的示例:
String[] types = {"mp3", "flac"};
Collection<File> files2 = FileUtils.listFiles(/path/to/your/dir, types , true);
答案 8 :(得分:2)
这样可以正常使用
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);
}
}
}
答案 9 :(得分:1)
此函数可能会列出其目录及其子目录中的所有文件名及其路径。
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]);
}
}
}
答案 10 :(得分:0)
使用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");
}
}
答案 11 :(得分:0)
另一个优化的代码
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());
}
}
答案 12 :(得分:0)
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());
}
}
答案 13 :(得分:0)
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);
}
}
答案 14 :(得分:0)
在Guava中,您不必等待Collection返回给您,但实际上可以迭代文件。很容易想象下面函数签名中的IDoSomethingWithThisFile
接口:
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还允许您在各种遍历样式之间。
答案 15 :(得分:0)
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);
}
}
答案 16 :(得分:0)
我在处理数百万个文件夹和文件时发现的更有效的方法是通过DOS命令在某个文件中捕获目录列表并解析它。一旦解析了数据,就可以进行分析和计算统计数据。
答案 17 :(得分:0)
就像你知道isDirectory()是一个非常慢的方法。我在文件浏览器中发现它很慢。我将查看一个库,用本机代码替换它。
答案 18 :(得分:0)
感觉它是愚蠢的访问 filesystem并获取内容 每个子目录而不是获取 一切都在。
你的感觉是对的。这就是文件系统的工作方式。没有更快的方法(除非您必须重复执行此操作或针对不同的模式,您可以将所有文件路径缓存在内存中,但是您必须处理缓存失效,即在添加/删除/重命名文件时会发生什么情况应用程序运行。)