想要有关这些文件的信息
Would like information about these files
我写了一个递归遍历目录的程序;也就是说,它有一个遍历目录中所有文件的方法;如果这些文件中的任何一个是(子)目录,它也会遍历子目录的文件等。一旦我的程序确定文件不是目录,它就会为它创建一个文件对象并从中获取某些信息,例如如何很多 space 它占用等
我想知道 NIO Path 对象是否会更快地完成这项工作;我不需要文件数据本身,afaik 我不需要打开文件。我需要名称、扩展名和长度。所以我着手编写一个新方法,它接受一个字符串并创建一个路径而不是一个文件。
我 运行 进入了一组令我困惑的文件。在 c:\Users\AppData\Local\Microsoft\WindowsApps\ 中有 5 个这样的文件;它们都是EXE文件,它们都是0长度。执行 DOS 目录列表(包括带有 /AL 限定符的目录)不会显示它们是连接点或 symlinks。但是,如果我用其中之一创建一个 Path 对象,然后尝试读取文件的基本属性:
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 命令适用于我自己创建的测试文件,但不适用于这些文件。我的程序对这个文件没有问题。
那么这些动物是什么?如果我在联结和符号 link 上执行 dir/al,它们通常会与 SYMLINK 和 JUNCTION 一起显示。我找到了关于 Hard Link 的文档,它似乎类似于符号 link;似乎没有在目录文件夹中显示这些的 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));
}
}
这些文件与 Windows Store Apps 的别名相关。这些允许从命令行启动 Windows Store 程序。您可以在此处查看别名并转别名 on/off:
Settings > Apps > App execution aliases
任何编辑都会更改文件夹中报告的 EXE 数量:
dir c:\Users\{userid}\AppData\Local\Microsoft\WindowsApps\
虽然使用 Files.readAttributes
returns 查询这些文件时出现异常,但您可以使用 Files.find
扫描它们,从而获得具有正确权限的相同信息。
这是一个例子 - 只需像上面那样传入你的目录,这会找到 returns 一个 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()
调用快得多。
我写了一个递归遍历目录的程序;也就是说,它有一个遍历目录中所有文件的方法;如果这些文件中的任何一个是(子)目录,它也会遍历子目录的文件等。一旦我的程序确定文件不是目录,它就会为它创建一个文件对象并从中获取某些信息,例如如何很多 space 它占用等
我想知道 NIO Path 对象是否会更快地完成这项工作;我不需要文件数据本身,afaik 我不需要打开文件。我需要名称、扩展名和长度。所以我着手编写一个新方法,它接受一个字符串并创建一个路径而不是一个文件。
我 运行 进入了一组令我困惑的文件。在 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 命令适用于我自己创建的测试文件,但不适用于这些文件。我的程序对这个文件没有问题。
那么这些动物是什么?如果我在联结和符号 link 上执行 dir/al,它们通常会与 SYMLINK 和 JUNCTION 一起显示。我找到了关于 Hard Link 的文档,它似乎类似于符号 link;似乎没有在目录文件夹中显示这些的 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));
}
}
这些文件与 Windows Store Apps 的别名相关。这些允许从命令行启动 Windows Store 程序。您可以在此处查看别名并转别名 on/off:
Settings > Apps > App execution aliases
任何编辑都会更改文件夹中报告的 EXE 数量:
dir c:\Users\{userid}\AppData\Local\Microsoft\WindowsApps\
虽然使用 Files.readAttributes
returns 查询这些文件时出现异常,但您可以使用 Files.find
扫描它们,从而获得具有正确权限的相同信息。
这是一个例子 - 只需像上面那样传入你的目录,这会找到 returns 一个 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()
调用快得多。