getCanonicalPath 和 toRealPath 之间的区别
Difference between getCanonicalPath and toRealPath
是否存在 File.getCanonicalPath() 和 File.toPath().toRealPath() 会产生不同结果的情况?
他们似乎都做了相当相似的事情,但文档从未真正说明他们应该做同样的事情。
是否存在我更喜欢一种方法而不是另一种方法的边界情况?
File.getAbsolutePath() 与 Path.toAbsolutePath() 相比如何 - 它们应该以相同的方式工作吗?
当然,下面的示例显示了一些差异。如果文件不存在,getCanonicalPath 也会抛出异常。
getCanonicalPath returns 规范或最简单形式的路径(来自 http://www.merriam-webster.com/dictionary/canonical%20form)
import java.io.File;
public class FileExample {
public static void main(String[] args) throws Exception {
File file = new File("/TEMP/../TEMP/myfile.txt");
System.out.println("ABS: " + file.getAbsolutePath());
System.out.println(" TO: " + file.toPath());
System.out.println("GET: " + file.getPath());
System.out.println("CAN: " + file.getCanonicalPath());
}
}
ABS: C:\TEMP\..\TEMP\myfile.txt
TO: \TEMP\..\TEMP\myfile.txt
GET: \TEMP\..\TEMP\myfile.txt
CAN: C:\TEMP\myfile.txt
规范路径是absolute and unique,但在不同的系统上会有不同的含义。
A canonical pathname is both absolute and unique. The precise definition of canonical form is system-dependent.
真正的路径是 the actual path with respect to the system. 无论您是否不处理符号链接,您都必须传入,其中它由 canonicalPath
隐式处理。
The precise definition of this method is implementation dependent but in general it derives from this path, an absolute path that locates the same file as this path, but with name elements that represent the actual name of the directories and the file. For example, where filename comparisons on a file system are case insensitive then the name elements represent the names in their actual case. Additionally, the resulting path has redundant name elements removed.
所以是的,这两种方法可以 return 不同,但这确实取决于您的系统。如果您需要一些独特的东西,那么 canonicalPath
是您最安全的选择,即使它不是 Path
.
API指出规范路径通常会删除冗余并解析符号链接等。
在 UNIX 机器上尝试以下操作:
File file = new File("../test.txt"); // execute from /tmp/java/example
file.getAbsolutePath(); // evaluates to /tmp/java/example/../test.txt
file.getCanonicalPath(); // evaluates to /tmp/java/test.txt
File 和 Path 的区别在于 Path 是较新的 NIO API 的一部分,它有很多改进并且更灵活。
例如,您可以使用 NIO 交换文件系统的实现(参见 https://github.com/google/jimfs),而 java.io.File 强制您在主机文件系统上操作。
我从测试中注意到的是
如果文件不存在,Path.toRealPath() 将抛出 java.nio.file.NoSuchFileException(Javadoc:Returns real 现有文件的路径。)
File.getCanonicalPath() 如果文件不存在则不会抛出异常(它只会抛出 IOException 如果
文件名本身无效,其中包含“\0”字符)。
因此,如果您想在实际创建文件之前将其用于路径检查,则前者不适合。
你是对的,这两种方法的 Javadoc 都有些肤浅。
结论:
getAbsolutePath
和 getPath
永远不会失败,因为它们不进行验证
getCanonicalPath
当来自 url 的驱动器盘符无效或与当前文件夹 不同时,结果无效
toPath().toRealPath()
正在检查有效性但是文件需要存在并且也可以跟随或不跟随符号链接
toPath()
足够安全,不需要文件存在。
.toPath().toAbsolutePath().normalize()
是最好的,不需要文件存在
我在 windows
中对@John 做了类似的测试
@Test
public void testCanonical() throws IOException {
test("d:tarGet\..\Target", "File exist and drive letter is on the current one");
test("d:tarGet\..\Target\.\..\", "File exist and drive letter is on the current one, but parent of current drive should exist");
test("d:tarGet\non-existent\..\..\Target\.\..\", "Relative path contains non-existent file");
test("d:target\\file", "Double slash");
test("c:tarGet\..\Target\.", "File doesn't exist and drive letter is on different drive than the current one");
test("l:tarGet\..\Target\.\..\", "Drive letter doesn't exist");
test("za:tarGet\..\Target\.\..\", "Drive letter is double so not valid");
test("d:tarGet|Suffix", "Path contains invalid chars in windows (|)");
test("d:tarGet\u0000Suffix", "Path contains invalid chars in both linux and windows (\0)");
}
private void test(String filename, String message) throws IOException {
java.io.File file = new java.io.File(filename);
System.out.println("Use: " + filename + " -> " + message);
System.out.println("F-GET: " + Try.of(() -> file.getPath()));
System.out.println("F-ABS: " + Try.of(() -> file.getAbsolutePath()));
System.out.println("F-CAN: " + Try.of(() -> file.getCanonicalPath()));
System.out.println("P-TO: " + Try.of(() -> file.toPath()));
System.out.println("P-ABS: " + Try.of(() -> file.toPath().toAbsolutePath()));
System.out.println("P-NOR: " + Try.of(() -> file.toPath().normalize()));
System.out.println("P-NOR-ABS: " + Try.of(() -> file.toPath().normalize().toAbsolutePath()));
System.out.println("P-ABS-NOR: " + Try.of(() -> file.toPath().toAbsolutePath().normalize()));
System.out.println("P-REAL: " + Try.of(() -> file.toPath().toRealPath()));
System.out.println("");
}
结果是:
Use: d:tarGet\..\Target -> File exist and drive letter is on the current one
F-GET: Success(d:tarGet\..\Target)
F-ABS: Success(d:\home\raiser\work\restfs\tarGet\..\Target)
F-CAN: Success(D:\home\raiser\work\restfs\target)
P-TO: Success(d:tarGet\..\Target)
P-ABS: Success(D:\home\raiser\work\restfs\tarGet\..\Target)
P-NOR: Success(d:Target)
P-NOR-ABS: Success(D:\home\raiser\work\restfs\Target)
P-ABS-NOR: Success(D:\home\raiser\work\restfs\Target)
P-REAL: Success(D:\home\raiser\work\restfs\target)
Use: d:tarGet\..\Target\.\..\ -> File exist and drive letter is on the current one, but parent of current drive should exist
F-GET: Success(d:tarGet\..\Target\.\..)
F-ABS: Success(d:\home\raiser\work\restfs\tarGet\..\Target\.\..)
F-CAN: Success(D:\home\raiser\work\restfs)
P-TO: Success(d:tarGet\..\Target\.\..)
P-ABS: Success(D:\home\raiser\work\restfs\tarGet\..\Target\.\..)
P-NOR: Success(d:)
P-NOR-ABS: Success(D:\home\raiser\work\restfs\)
P-ABS-NOR: Success(D:\home\raiser\work\restfs)
P-REAL: Success(D:\home\raiser\work\restfs)
Use: d:tarGet\non-existent\..\..\Target\.\..\ -> Relative path contains non-existent file
F-GET: Success(d:tarGet\non-existent\..\..\Target\.\..)
F-ABS: Success(d:\home\raiser\work\restfs\tarGet\non-existent\..\..\Target\.\..)
F-CAN: Success(D:\home\raiser\work\restfs)
P-TO: Success(d:tarGet\non-existent\..\..\Target\.\..)
P-ABS: Success(D:\home\raiser\work\restfs\tarGet\non-existent\..\..\Target\.\..)
P-NOR: Success(d:)
P-NOR-ABS: Success(D:\home\raiser\work\restfs\)
P-ABS-NOR: Success(D:\home\raiser\work\restfs)
P-REAL: Success(D:\home\raiser\work\restfs)
Use: d:target\file -> Double slash
F-GET: Success(d:target\file)
F-ABS: Success(d:\home\raiser\work\restfs\target\file)
F-CAN: Success(D:\home\raiser\work\restfs\target\file)
P-TO: Success(d:target\file)
P-ABS: Success(D:\home\raiser\work\restfs\target\file)
P-NOR: Success(d:target\file)
P-NOR-ABS: Success(D:\home\raiser\work\restfs\target\file)
P-ABS-NOR: Success(D:\home\raiser\work\restfs\target\file)
P-REAL: Failure(java.nio.file.NoSuchFileException: D:\home\raiser\work\restfs\target\file)
Use: c:tarGet\..\Target\. -> File doesn't exist and drive letter is on different drive than the current one
F-GET: Success(c:tarGet\..\Target\.)
F-ABS: Success(c:\tarGet\..\Target\.)
F-CAN: Success(C:\Target)
P-TO: Success(c:tarGet\..\Target\.)
P-ABS: Success(C:\tarGet\..\Target\.)
P-NOR: Success(c:Target)
P-NOR-ABS: Success(C:\Target)
P-ABS-NOR: Success(C:\Target)
P-REAL: Failure(java.nio.file.NoSuchFileException: C:\Target)
Use: l:tarGet\..\Target\.\..\ -> Drive letter doesn't exist
F-GET: Success(l:tarGet\..\Target\.\..)
F-ABS: Success(l:\tarGet\..\Target\.\..)
F-CAN: Success(L:\)
P-TO: Success(l:tarGet\..\Target\.\..)
P-ABS: Failure(java.io.IOError: java.io.IOException: Unable to get working directory of drive 'L')
P-NOR: Success(l:)
P-NOR-ABS: Failure(java.io.IOError: java.io.IOException: Unable to get working directory of drive 'L')
P-ABS-NOR: Failure(java.io.IOError: java.io.IOException: Unable to get working directory of drive 'L')
P-REAL: Failure(java.io.IOException: Unable to get working directory of drive 'L')
Use: za:tarGet\..\Target\.\..\ -> Drive letter is double so not valid
F-GET: Success(za:tarGet\..\Target\.\..)
F-ABS: Success(D:\home\raiser\work\restfs\za:tarGet\..\Target\.\..)
F-CAN: Success(D:\home\raiser\work\restfs)
P-TO: Failure(java.nio.file.InvalidPathException: Illegal char <:> at index 2: za:tarGet\..\Target\.\..)
P-ABS: Failure(java.nio.file.InvalidPathException: Illegal char <:> at index 2: za:tarGet\..\Target\.\..)
P-NOR: Failure(java.nio.file.InvalidPathException: Illegal char <:> at index 2: za:tarGet\..\Target\.\..)
P-NOR-ABS: Failure(java.nio.file.InvalidPathException: Illegal char <:> at index 2: za:tarGet\..\Target\.\..)
P-ABS-NOR: Failure(java.nio.file.InvalidPathException: Illegal char <:> at index 2: za:tarGet\..\Target\.\..)
P-REAL: Failure(java.nio.file.InvalidPathException: Illegal char <:> at index 2: za:tarGet\..\Target\.\..)
Use: d:tarGet|Suffix -> Path contains invalid chars in windows (|)
F-GET: Success(d:tarGet|Suffix)
F-ABS: Success(d:\home\raiser\work\restfs\tarGet|Suffix)
F-CAN: Failure(java.io.IOException: The filename, directory name, or volume label syntax is incorrect)
P-TO: Failure(java.nio.file.InvalidPathException: Illegal char <|> at index 8: d:tarGet|Suffix)
P-ABS: Failure(java.nio.file.InvalidPathException: Illegal char <|> at index 8: d:tarGet|Suffix)
P-NOR: Failure(java.nio.file.InvalidPathException: Illegal char <|> at index 8: d:tarGet|Suffix)
P-NOR-ABS: Failure(java.nio.file.InvalidPathException: Illegal char <|> at index 8: d:tarGet|Suffix)
P-ABS-NOR: Failure(java.nio.file.InvalidPathException: Illegal char <|> at index 8: d:tarGet|Suffix)
P-REAL: Failure(java.nio.file.InvalidPathException: Illegal char <|> at index 8: d:tarGet|Suffix)
是否存在 File.getCanonicalPath() 和 File.toPath().toRealPath() 会产生不同结果的情况? 他们似乎都做了相当相似的事情,但文档从未真正说明他们应该做同样的事情。 是否存在我更喜欢一种方法而不是另一种方法的边界情况? File.getAbsolutePath() 与 Path.toAbsolutePath() 相比如何 - 它们应该以相同的方式工作吗?
当然,下面的示例显示了一些差异。如果文件不存在,getCanonicalPath 也会抛出异常。
getCanonicalPath returns 规范或最简单形式的路径(来自 http://www.merriam-webster.com/dictionary/canonical%20form)
import java.io.File;
public class FileExample {
public static void main(String[] args) throws Exception {
File file = new File("/TEMP/../TEMP/myfile.txt");
System.out.println("ABS: " + file.getAbsolutePath());
System.out.println(" TO: " + file.toPath());
System.out.println("GET: " + file.getPath());
System.out.println("CAN: " + file.getCanonicalPath());
}
}
ABS: C:\TEMP\..\TEMP\myfile.txt
TO: \TEMP\..\TEMP\myfile.txt
GET: \TEMP\..\TEMP\myfile.txt
CAN: C:\TEMP\myfile.txt
规范路径是absolute and unique,但在不同的系统上会有不同的含义。
A canonical pathname is both absolute and unique. The precise definition of canonical form is system-dependent.
真正的路径是 the actual path with respect to the system. 无论您是否不处理符号链接,您都必须传入,其中它由 canonicalPath
隐式处理。
The precise definition of this method is implementation dependent but in general it derives from this path, an absolute path that locates the same file as this path, but with name elements that represent the actual name of the directories and the file. For example, where filename comparisons on a file system are case insensitive then the name elements represent the names in their actual case. Additionally, the resulting path has redundant name elements removed.
所以是的,这两种方法可以 return 不同,但这确实取决于您的系统。如果您需要一些独特的东西,那么 canonicalPath
是您最安全的选择,即使它不是 Path
.
API指出规范路径通常会删除冗余并解析符号链接等。
在 UNIX 机器上尝试以下操作:
File file = new File("../test.txt"); // execute from /tmp/java/example
file.getAbsolutePath(); // evaluates to /tmp/java/example/../test.txt
file.getCanonicalPath(); // evaluates to /tmp/java/test.txt
File 和 Path 的区别在于 Path 是较新的 NIO API 的一部分,它有很多改进并且更灵活。
例如,您可以使用 NIO 交换文件系统的实现(参见 https://github.com/google/jimfs),而 java.io.File 强制您在主机文件系统上操作。
我从测试中注意到的是
-
如果文件不存在,
Path.toRealPath() 将抛出 java.nio.file.NoSuchFileException(Javadoc:Returns real 现有文件的路径。)
File.getCanonicalPath() 如果文件不存在则不会抛出异常(它只会抛出 IOException 如果 文件名本身无效,其中包含“\0”字符)。
因此,如果您想在实际创建文件之前将其用于路径检查,则前者不适合。
你是对的,这两种方法的 Javadoc 都有些肤浅。
结论:
getAbsolutePath
和getPath
永远不会失败,因为它们不进行验证getCanonicalPath
当来自 url 的驱动器盘符无效或与当前文件夹 不同时,结果无效
toPath().toRealPath()
正在检查有效性但是文件需要存在并且也可以跟随或不跟随符号链接toPath()
足够安全,不需要文件存在。.toPath().toAbsolutePath().normalize()
是最好的,不需要文件存在
我在 windows
中对@John 做了类似的测试 @Test
public void testCanonical() throws IOException {
test("d:tarGet\..\Target", "File exist and drive letter is on the current one");
test("d:tarGet\..\Target\.\..\", "File exist and drive letter is on the current one, but parent of current drive should exist");
test("d:tarGet\non-existent\..\..\Target\.\..\", "Relative path contains non-existent file");
test("d:target\\file", "Double slash");
test("c:tarGet\..\Target\.", "File doesn't exist and drive letter is on different drive than the current one");
test("l:tarGet\..\Target\.\..\", "Drive letter doesn't exist");
test("za:tarGet\..\Target\.\..\", "Drive letter is double so not valid");
test("d:tarGet|Suffix", "Path contains invalid chars in windows (|)");
test("d:tarGet\u0000Suffix", "Path contains invalid chars in both linux and windows (\0)");
}
private void test(String filename, String message) throws IOException {
java.io.File file = new java.io.File(filename);
System.out.println("Use: " + filename + " -> " + message);
System.out.println("F-GET: " + Try.of(() -> file.getPath()));
System.out.println("F-ABS: " + Try.of(() -> file.getAbsolutePath()));
System.out.println("F-CAN: " + Try.of(() -> file.getCanonicalPath()));
System.out.println("P-TO: " + Try.of(() -> file.toPath()));
System.out.println("P-ABS: " + Try.of(() -> file.toPath().toAbsolutePath()));
System.out.println("P-NOR: " + Try.of(() -> file.toPath().normalize()));
System.out.println("P-NOR-ABS: " + Try.of(() -> file.toPath().normalize().toAbsolutePath()));
System.out.println("P-ABS-NOR: " + Try.of(() -> file.toPath().toAbsolutePath().normalize()));
System.out.println("P-REAL: " + Try.of(() -> file.toPath().toRealPath()));
System.out.println("");
}
结果是:
Use: d:tarGet\..\Target -> File exist and drive letter is on the current one
F-GET: Success(d:tarGet\..\Target)
F-ABS: Success(d:\home\raiser\work\restfs\tarGet\..\Target)
F-CAN: Success(D:\home\raiser\work\restfs\target)
P-TO: Success(d:tarGet\..\Target)
P-ABS: Success(D:\home\raiser\work\restfs\tarGet\..\Target)
P-NOR: Success(d:Target)
P-NOR-ABS: Success(D:\home\raiser\work\restfs\Target)
P-ABS-NOR: Success(D:\home\raiser\work\restfs\Target)
P-REAL: Success(D:\home\raiser\work\restfs\target)
Use: d:tarGet\..\Target\.\..\ -> File exist and drive letter is on the current one, but parent of current drive should exist
F-GET: Success(d:tarGet\..\Target\.\..)
F-ABS: Success(d:\home\raiser\work\restfs\tarGet\..\Target\.\..)
F-CAN: Success(D:\home\raiser\work\restfs)
P-TO: Success(d:tarGet\..\Target\.\..)
P-ABS: Success(D:\home\raiser\work\restfs\tarGet\..\Target\.\..)
P-NOR: Success(d:)
P-NOR-ABS: Success(D:\home\raiser\work\restfs\)
P-ABS-NOR: Success(D:\home\raiser\work\restfs)
P-REAL: Success(D:\home\raiser\work\restfs)
Use: d:tarGet\non-existent\..\..\Target\.\..\ -> Relative path contains non-existent file
F-GET: Success(d:tarGet\non-existent\..\..\Target\.\..)
F-ABS: Success(d:\home\raiser\work\restfs\tarGet\non-existent\..\..\Target\.\..)
F-CAN: Success(D:\home\raiser\work\restfs)
P-TO: Success(d:tarGet\non-existent\..\..\Target\.\..)
P-ABS: Success(D:\home\raiser\work\restfs\tarGet\non-existent\..\..\Target\.\..)
P-NOR: Success(d:)
P-NOR-ABS: Success(D:\home\raiser\work\restfs\)
P-ABS-NOR: Success(D:\home\raiser\work\restfs)
P-REAL: Success(D:\home\raiser\work\restfs)
Use: d:target\file -> Double slash
F-GET: Success(d:target\file)
F-ABS: Success(d:\home\raiser\work\restfs\target\file)
F-CAN: Success(D:\home\raiser\work\restfs\target\file)
P-TO: Success(d:target\file)
P-ABS: Success(D:\home\raiser\work\restfs\target\file)
P-NOR: Success(d:target\file)
P-NOR-ABS: Success(D:\home\raiser\work\restfs\target\file)
P-ABS-NOR: Success(D:\home\raiser\work\restfs\target\file)
P-REAL: Failure(java.nio.file.NoSuchFileException: D:\home\raiser\work\restfs\target\file)
Use: c:tarGet\..\Target\. -> File doesn't exist and drive letter is on different drive than the current one
F-GET: Success(c:tarGet\..\Target\.)
F-ABS: Success(c:\tarGet\..\Target\.)
F-CAN: Success(C:\Target)
P-TO: Success(c:tarGet\..\Target\.)
P-ABS: Success(C:\tarGet\..\Target\.)
P-NOR: Success(c:Target)
P-NOR-ABS: Success(C:\Target)
P-ABS-NOR: Success(C:\Target)
P-REAL: Failure(java.nio.file.NoSuchFileException: C:\Target)
Use: l:tarGet\..\Target\.\..\ -> Drive letter doesn't exist
F-GET: Success(l:tarGet\..\Target\.\..)
F-ABS: Success(l:\tarGet\..\Target\.\..)
F-CAN: Success(L:\)
P-TO: Success(l:tarGet\..\Target\.\..)
P-ABS: Failure(java.io.IOError: java.io.IOException: Unable to get working directory of drive 'L')
P-NOR: Success(l:)
P-NOR-ABS: Failure(java.io.IOError: java.io.IOException: Unable to get working directory of drive 'L')
P-ABS-NOR: Failure(java.io.IOError: java.io.IOException: Unable to get working directory of drive 'L')
P-REAL: Failure(java.io.IOException: Unable to get working directory of drive 'L')
Use: za:tarGet\..\Target\.\..\ -> Drive letter is double so not valid
F-GET: Success(za:tarGet\..\Target\.\..)
F-ABS: Success(D:\home\raiser\work\restfs\za:tarGet\..\Target\.\..)
F-CAN: Success(D:\home\raiser\work\restfs)
P-TO: Failure(java.nio.file.InvalidPathException: Illegal char <:> at index 2: za:tarGet\..\Target\.\..)
P-ABS: Failure(java.nio.file.InvalidPathException: Illegal char <:> at index 2: za:tarGet\..\Target\.\..)
P-NOR: Failure(java.nio.file.InvalidPathException: Illegal char <:> at index 2: za:tarGet\..\Target\.\..)
P-NOR-ABS: Failure(java.nio.file.InvalidPathException: Illegal char <:> at index 2: za:tarGet\..\Target\.\..)
P-ABS-NOR: Failure(java.nio.file.InvalidPathException: Illegal char <:> at index 2: za:tarGet\..\Target\.\..)
P-REAL: Failure(java.nio.file.InvalidPathException: Illegal char <:> at index 2: za:tarGet\..\Target\.\..)
Use: d:tarGet|Suffix -> Path contains invalid chars in windows (|)
F-GET: Success(d:tarGet|Suffix)
F-ABS: Success(d:\home\raiser\work\restfs\tarGet|Suffix)
F-CAN: Failure(java.io.IOException: The filename, directory name, or volume label syntax is incorrect)
P-TO: Failure(java.nio.file.InvalidPathException: Illegal char <|> at index 8: d:tarGet|Suffix)
P-ABS: Failure(java.nio.file.InvalidPathException: Illegal char <|> at index 8: d:tarGet|Suffix)
P-NOR: Failure(java.nio.file.InvalidPathException: Illegal char <|> at index 8: d:tarGet|Suffix)
P-NOR-ABS: Failure(java.nio.file.InvalidPathException: Illegal char <|> at index 8: d:tarGet|Suffix)
P-ABS-NOR: Failure(java.nio.file.InvalidPathException: Illegal char <|> at index 8: d:tarGet|Suffix)
P-REAL: Failure(java.nio.file.InvalidPathException: Illegal char <|> at index 8: d:tarGet|Suffix)