我写了一个递归遍历目录的程序;也就是说,它有一个遍历目录中所有文件的方法;如果这些文件中的任何一个是(子)目录,它也会通过子目录的文件等。一旦我的程序确定一个文件不是一个目录,它就会为它创建一个 File 对象并从中获取某些信息,例如如何占用多少空间等
我想知道 NIO Path 对象是否能更快地完成这项工作;我不需要文件数据本身,我不需要打开文件。我需要名称、扩展名和长度。所以我开始编写一个新方法,它接受一个字符串并创建一个路径而不是一个文件。
我遇到了一组令我困惑的文件。在 c:\Users
Path path = Paths.get(folderPath.toString(), fileName);
BasicFileAttributes bfa = null;
try { bfa = Files.readAttributes(path, BasicFileAttributes.class); }
catch (IOException ioe)
{
File testFile = new File(String.format("%s%s%s", folderPath.toString(), File.separator, fileName));
System.out.println(String.format("Got %s even though IOException thrown: %s", testFile.getName(), ioe.getMessage()));
}
我明白了:
C:\Users\ralph\AppData\Local\Microsoft\WindowsApps\GameBarElevatedFT_Alias.exe: The file cannot be accessed by the system.
at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:86)
at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)
at sun.nio.fs.WindowsFileAttributeViews$Basic.readAttributes(WindowsFileAttributeViews.java:53)
at sun.nio.fs.WindowsFileAttributeViews$Basic.readAttributes(WindowsFileAttributeViews.java:38)
at sun.nio.fs.WindowsFileSystemProvider.readAttributes(WindowsFileSystemProvider.java:193)
at java.nio.file.Files.readAttributes(Files.java:1737)
at rcutil.file.FolderWalker.walkTree(FolderWalker.java:80)
at rcutil.file.FolderWalker.walkTree(FolderWalker.java:89)
at rcutil.file.FolderWalker.walkTree(FolderWalker.java:89)
at rcutil.file.FolderWalker.walkTree(FolderWalker.java:89)
at rcutil.file.FolderWalker.walkFolder(FolderWalker.java:60)
at rcutil.file.FolderWalker.main(FolderWalker.java:178)
(我将在下面提供完整代码)
我创建了一个带有 EXE 扩展名的 0 长度文件;它显示在目录列表中,但如果我执行 dir/al 则不会。 attrib 命令适用于我自己创建的测试文件,但不适用于这些文件。我的程序对这个文件没有问题。
那么这些动物是什么?如果我在结点和符号链接上执行 dir/al,它们通常会与 SYMLINK 和 JUNCTION 一起显示。我找到了关于硬链接的文档,它似乎类似于符号链接;似乎没有 DOS(或其他)命令在目录文件夹中显示这些。
有谁有更多的信息吗?我总是可以回到使用 java.io.File;不知道 java.nio.file.Path 是否会更快。但现在我很好奇这些野兽是什么......
package rcutil.file;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import rcutil.system.OperatingSystemUtil;
/**
* Provides the walking of all files in a folder, recursively; instantiation requires
* a parameter class implementing a callback to receive a method call for each file
* and directory encountered in the tree walk.
* <P>The walkFolder() method in this class walks an entire directory subtree recursively,
* making a call to the given callback class for each file and directory giving information
* about the file or directory found.
*
* <P> TODO: convert this to the java.nio version, which I didn't know existed when
* I wrote this.
* @author rcook
*
*/
public class FolderWalker
{
FolderWalkerUser user;
/**
* Instantiate the folder walker to call back the given user for each file and directory encountered.
*
* @param user
*/
public FolderWalker(FolderWalkerUser user)
{
this.user = user;
}
/**
* Start a treewalk at the given folder. Cannot be a non-directory file or a symbolic link.
* @param givenFolderName
* @throws Exception
*/
public void walkFolder(String givenFolderName) throws Exception
{
try
{
if (isStringEmpty(givenFolderName)) { throwIOException((givenFolderName == null ? "Null" : "Empty") + " folderPath given to FolderWalker"); }
Path path = Paths.get(givenFolderName);
File topFile = path.toFile(); // new File(givenFolderName);
if (!(topFile.exists())) { throwIOException("File <" + givenFolderName + "> given to FolderWalker does not exist"); }
BasicFileAttributes bfa = Files.readAttributes(path, BasicFileAttributes.class);
if (!bfa.isDirectory()) { throwIllegalArgumentException("Folder path <" + givenFolderName + "> given to FolderWalker is not a directory"); }
if (bfa.isSymbolicLink()) { throwIllegalArgumentException("Folder path <" + givenFolderName + "> given to FolderWalker is a symbolic link"); }
// walkTree(topFile);
walkTree(path);
user.afterFolderWalk(givenFolderName);
} catch (IOException e)
{
throw new IOException("Exception during FolderWalker.walkFolder() for <" + givenFolderName + ">", e);
}
}
private void walkTree(Path folderPath) throws Exception
{
File folderFile = folderPath.toFile();
user.foundFile(folderFile, true);
String[] fileNameList = folderFile.list();
if (fileNameList != null && fileNameList.length > 0)
{
for (String fileName : fileNameList)
{
Path path = Paths.get(folderPath.toString(), fileName);
BasicFileAttributes bfa = null;
try { bfa = Files.readAttributes(path, BasicFileAttributes.class); }
catch (IOException ioe)
{
File testFile = new File(String.format("%s%s%s", folderPath.toString(), File.separator, fileName));
System.out.println(String.format("Got %s even though IOException thrown: %s", testFile.getName(), ioe.getMessage()));
// ioe.printStackTrace();
}
if (bfa != null)
{
if (bfa.isDirectory()) { walkTree(path); }
else { user.foundFile(null, false); }
// BIG TODO: passing null; if we switch to Paths, do we need to change the
// FolderWalkerUser interface to use paths?
// trying to avoid File when possible, on theory that doing so saves runtime
}
}
}
}
private void walkTree(File folder) throws Exception
{
// System.out.println("walkTree thread name: " + Thread.currentThread().getName());
user.foundFile(folder, true); // users are notified of folders at the top of their subtree
String[] fileNameList = folder.list();
if (fileNameList != null && fileNameList.length > 0)
{
for (String fileName : fileNameList)
{
File currentFile = new File(folder, fileName);
// if (isSymbolicLink(currentFile)) { System.out.printf("Symbolic link: %s%n"); }
if (currentFile.isDirectory())
{
walkTree(currentFile);
}
else
{
user.foundFile(currentFile, false); // user notified of non-folder files in sequence, after their containing folder
}
}
}
}
private boolean isStringEmpty(String givenFolderName)
{
return (givenFolderName == null || givenFolderName.length() <= 0);
}
private void throwIOException(String msg) throws IOException
{
throw new IOException(msg);
}
private void throwIllegalArgumentException(String msg) throws IOException
{
throw new IllegalArgumentException(msg);
}
private boolean isSymbolicLink(File file) throws IOException
{
String absolutePath = file.getAbsolutePath();
String canonicalPath = file.getCanonicalPath();
if (OperatingSystemUtil.isWindows())
{
absolutePath = absolutePath.toLowerCase();
canonicalPath = canonicalPath.toLowerCase();
}
return (!(absolutePath.equals(canonicalPath)));
}
/**
* for testing purposes -- echo the folder tree as we walk it.
* @param arguments
*/
public static void main(String ... arguments)
{
String startFolderName = "C:\\Users\\ralph\\AppData";
if (arguments != null && arguments.length > 0)
{
startFolderName = arguments[0];
}
System.out.println("Starting walk...");
long startTime = System.currentTimeMillis();
FolderWalker walker = new FolderWalker(new FolderWalkerUser()
{
int fileCount, folderCount;
public void foundFile(File file, boolean isDirectory)
{
// System.out.println(file.getAbsolutePath());
fileCount++;
}
public void afterFolderWalk(String folderName)
{
// System.out.println("all done, folder name " + folderName);
folderCount++;
}
}
);
try { walker.walkFolder(startFolderName); }
catch (Exception ioe) { ioe.printStackTrace(); }
long elapsedTime = System.currentTimeMillis() - startTime;
System.out.println(String.format("Walk done, %5.2f seconds", elapsedTime/1000f));
}
}
答案 0 :(得分:2)
这些文件与 Windows 应用商店应用的别名有关。这些允许从命令行启动 Windows 应用商店程序。您可以在此处查看别名并打开/关闭别名:
Settings > Apps > App execution aliases
任何编辑都会更改文件夹中报告的 EXE 数量:
dir c:\Users\{userid}\AppData\Local\Microsoft\WindowsApps\
尽管使用 Files.readAttributes
查询这些文件会返回异常,但您可以使用 Files.find
扫描它们,这会以正确的权限获得相同的信息。
这是一个示例 - 只需按上述方式传入您的目录,此查找将返回一个 Map.Entry<Path, BasicFileAttributes>
对流,以便您可以读取每个 EXE 的路径及其属性:
public static Stream<Map.Entry<Path, BasicFileAttributes>>
find(Path dir, int maxDepth, BiPredicate<Path, BasicFileAttributes> matcher, FileVisitOption... options) throws IOException {
HashMap <Path,BasicFileAttributes> attrs = new HashMap<>();
BiPredicate<Path, BasicFileAttributes> predicate = (p,a) -> (matcher == null || matcher.test(p, a)) && attrs.put(p, a) == null;
return Files.find(dir, maxDepth, predicate, options).map(p -> Map.entry(p, attrs.remove(p)));
}
public static void main(String[] args) throws IOException {
Path dir = Path.of(args[0]);
// replace Integer.MAX_VALUE by 1 for current dir only:
try(var x = find(dir, Integer.MAX_VALUE, (p,a)-> p.toString().toLowerCase().endsWith(".exe"))) {
x.forEach(entry -> System.out.println(entry.getKey().toString()+" => "
+(entry.getValue().isDirectory() ? "DIR" : String.valueOf(entry.getValue().size()))));
}
}
使用 Files.find/walk
/ NIO 进行目录扫描,对于更大的目录树,NIO 比重复的 File.list()
调用要快得多。