Java 中有没有办法在不尝试创建文件的情况下确定路径是否有效?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/468789/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there a way in Java to determine if a path is valid without attempting to create a file?
提问by Raibaz
I need to determine if a user-supplied string is a valid file path (i.e., if createNewFile()
will succeed or throw an Exception) but I don't want to bloat the file system with useless files, created just for validation purposes.
我需要确定用户提供的字符串是否是有效的文件路径(即,是否createNewFile()
会成功或抛出异常),但我不想使用无用的文件来膨胀文件系统,这些文件仅为验证目的而创建。
Is there a way to determine if the string I have is a valid file path without attempting to create the file?
有没有办法在不尝试创建文件的情况下确定我拥有的字符串是否是有效的文件路径?
I know the definition of "valid file path" varies depending on the OS, but I was wondering if there was any quick way of accepting C:/foo
or /foo
and rejecting banana
.
我知道“有效文件路径”的定义因操作系统而异,但我想知道是否有任何快速接受C:/foo
或/foo
拒绝banana
.
A possible approach may be attempting to create the file and eventually deleting it if the creation succeeded, but I hope there is a more elegant way of achieving the same result.
一种可能的方法可能是尝试创建文件并在创建成功后最终将其删除,但我希望有一种更优雅的方式来实现相同的结果。
采纳答案by krosenvold
This would check for the existance of the directory as well.
这也将检查目录是否存在。
File file = new File("c:\cygwin\cygwin.bat");
if (!file.isDirectory())
file = file.getParentFile();
if (file.exists()){
...
}
It seems like file.canWrite() does not give you a clear indication if you have permissions to write to the directory.
如果您有写入目录的权限,似乎 file.canWrite() 没有给您明确的指示。
回答by cletus
A number of things can go wrong when you try and create a file:
当您尝试创建文件时,可能会出现许多问题:
- Your lack the requisite permissions;
- There is not enough space on the device;
- The device experiences an error;
- Some policy of custom security prohibits you from creating a file of a particular type;
- etc.
- 您缺乏必要的权限;
- 设备上没有足够的空间;
- 设备出现错误;
- 某些自定义安全策略禁止您创建特定类型的文件;
- 等等。
More to the point, those can change between when you try and query to see if you can and when you actually can. In a multithreaded environment this is one of the primary causes of race conditions and can be a real vulnerability of some programs.
更重要的是,这些可以在您尝试查询以查看您是否可以和您实际可以之间发生变化。在多线程环境中,这是竞争条件的主要原因之一,并且可能是某些程序的真正漏洞。
Basically you just have to try and create it and see if it works. And that's the correct way to do it. It's why things like ConcurrentHashMap
has a putIfAbsent()
so the check and insert is an atomic operation and doesn't suffer from race conditions. Exactly the same principle is in play here.
基本上你只需要尝试创建它,看看它是否有效。这就是正确的做法。这就是为什么像ConcurrentHashMap
has aputIfAbsent()
这样的检查和插入是一个原子操作并且不会受到竞争条件的影响。完全相同的原则在这里发挥作用。
If this is just part of some diagnostic or install process, just do it and see if it works. Again there's no guarantee that it'll work later however.
如果这只是某些诊断或安装过程的一部分,请执行它并查看它是否有效。但是,再次不能保证它会在以后工作。
Basically your program has to be robust enough to die gracefully if it can't write a relevant file.
基本上,您的程序必须足够健壮,才能在无法写入相关文件的情况下优雅地死掉。
回答by Zach Scrivena
File.getCanonicalPath()
is quite useful for this purpose. IO exceptions are thrown for certain types of invalid filenames (e.g. CON
, PRN
, *?*
in Windows) when resolving against the OS or file system. However, this only serves as a preliminary check; you will still need to handle other failures when actually creating the file (e.g. insufficient permissions, lack of drive space, security restrictions).
File.getCanonicalPath()
为此目的非常有用。IO抛出异常某些类型的无效文件名(例如CON
,PRN
,*?*
在Windows中)针对OS或文件系统解析时。然而,这仅作为初步检查;在实际创建文件时,您仍然需要处理其他故障(例如权限不足、驱动器空间不足、安全限制)。
回答by sullivan-
boolean canWrite(File file) {
if (file.exists()) {
return file.canWrite();
}
else {
try {
file.createNewFile();
file.delete();
return true;
}
catch (Exception e) {
return false;
}
}
}
回答by c0der
Path class introduced in Java 7 adds new alternatives, like the following:
(Does notwork properly under Linux- always returns true)
在Java 7中引入了Path类增加了新的选择,如下所示:
(不不是下正常工作的Linux-总是返回true)
/**
* <pre>
* Checks if a string is a valid path.
* Null safe.
*
* Calling examples:
* isValidPath("c:/test"); //returns true
* isValidPath("c:/te:t"); //returns false
* isValidPath("c:/te?t"); //returns false
* isValidPath("c/te*t"); //returns false
* isValidPath("good.txt"); //returns true
* isValidPath("not|good.txt"); //returns false
* isValidPath("not:good.txt"); //returns false
* </pre>
*/
public static boolean isValidPath(String path) {
try {
Paths.get(path);
} catch (InvalidPathException | NullPointerException ex) {
return false;
}
return true;
}
回答by Fangming
Here's something you can do that works across operating systems
以下是您可以跨操作系统执行的操作
Using regex match to check for existing known invalid characters.
使用正则表达式匹配来检查现有的已知无效字符。
if (newName.matches(".*[/\n\r\tvar myFile = new File("/home/me/maybe/write/here.log")
if (canCreateOrIsWritable(myFile)) {
// We're good. Create the file or append to it
createParents(myFile);
appendOrCreate(myFile, "new content");
} else {
// Let's pick another destination. Maybe the OS's temporary directory:
var tempDir = System.getProperty("java.io.tmpdir");
var alternative = Paths.get(tempDir, "second_choice.log");
appendOrCreate(alternative, "new content in temporary directory");
}
\f`?*\<>|\":].*")) {
System.out.println("Invalid!");
} else {
System.out.println("Valid!");
}
Pros
优点
- This works across operating systems
- You can customize it whatever way you want by editing that regex.
- 这适用于跨操作系统
- 您可以通过编辑该正则表达式以任何方式自定义它。
Cons
缺点
- This might not be a complete list and need more research to fill in more invalid patterns or characters.
- 这可能不是一个完整的列表,需要更多的研究来填充更多无效的模式或字符。
回答by Matthias Braun
Just do it (and clean up after yourself)
就这样做(然后自己清理)
A possible approach may be attempting to create the file and eventually deleting it if the creation succeeded, but I hope there is a more elegant way of achieving the same result.
一种可能的方法可能是尝试创建文件并在创建成功后最终将其删除,但我希望有一种更优雅的方式来实现相同的结果。
Maybe that's the most robust way.
也许这是最稳健的方式。
Below is canCreateOrIsWritable
that determines whether your program is able to create a file and its parent directoriesat a given path, or, if there's already a file there, write to it.
下面是canCreateOrIsWritable
确定您的程序是否能够在给定路径上创建文件及其父目录,或者,如果那里已经有一个文件,则写入它。
It does so by actually creating the necessary parent directories as well as an empty file at the path. Afterwards, it deletes them (if there existed a file at the path, it's left alone).
它通过实际创建必要的父目录以及路径中的空文件来实现。之后,它会删除它们(如果路径中存在文件,则将其保留)。
Here's how you might use it:
以下是您可以如何使用它:
static boolean canCreateOrIsWritable(File file) {
boolean canCreateOrIsWritable;
// The non-existent ancestor directories of the file.
// The file's parent directory is first
List<File> parentDirsToCreate = getParentDirsToCreate(file);
// Create the parent directories that don't exist, starting with the one
// highest up in the file system hierarchy (closest to root, farthest
// away from the file)
reverse(parentDirsToCreate).forEach(File::mkdir);
try {
boolean wasCreated = file.createNewFile();
if (wasCreated) {
canCreateOrIsWritable = true;
// Remove the file and its parent dirs that didn't exist before
file.delete();
parentDirsToCreate.forEach(File::delete);
} else {
// There was already a file at the path → Let's see if we can
// write to it
canCreateOrIsWritable = java.nio.file.Files.isWritable(file.toPath());
}
} catch (IOException e) {
// File creation failed
canCreateOrIsWritable = false;
}
return canCreateOrIsWritable;
}
static List<File> getParentDirsToCreate(File file) {
var parentsToCreate = new ArrayList<File>();
File parent = file.getParentFile();
while (parent != null && !parent.exists()) {
parentsToCreate.add(parent);
parent = parent.getParentFile();
}
return parentsToCreate;
}
static <T> List<T> reverse(List<T> input) {
var reversed = new ArrayList<T>();
for (int i = input.size() - 1; i >= 0; i--) {
reversed.add(input.get(i));
}
return reversed;
}
static void createParents(File file) {
File parent = file.getParentFile();
if (parent != null) {
parent.mkdirs();
}
}
The essential method with a few helper methods:
带有一些辅助方法的基本方法:
##代码##Keep in mind that between calling canCreateOrIsWritable
and creating the actual file, the contents and permissions of your file system might have changed.
请记住,在调用canCreateOrIsWritable
和创建实际文件之间,文件系统的内容和权限可能已更改。