在 Java nio 中复制后我可以得到 sameFile 为真吗?

Can I get sameFile is true after copy in Java nio?

当Files.isSameFile从fileA复制fileB后调用时,我认为结果是true,因为fileA与fileB相同。然而,结果是 false.

Path pathA = Path.of("A.txt");
Path pathB = Path.of("B.txt");

Files.copy(pathA, pathB);

if(Files.isSameFile(pathA, pathB))
    System.out.println("file is same"); 
else
    System.out.println("file is not same"); // the result

我发现 Files.isSameFile 只比较路径字符串。

copy后如何得到true?我要

Path pathA = Path.of("A.txt");
Path pathB = Path.of("B.txt");

Files.copy(pathA, pathB);

if(??) <-- I want
    System.out.println("file is same"); 

I found that Files.isSameFile compare only String of Path.

不完全是。 Javadoc 表示:

If both Path objects are equal then this method returns true without checking if the file exists. If the two Path objects are associated with different providers then this method returns false. Otherwise, this method checks if both Path objects locate the same file, and depending on the implementation, may require to open or access both files.

方法isSameFile()不比较内容,意思是定义是否two paths point to the same location in the file system.

如果根据 equals()[=54= 两个路径相同,此方法将 return true ] 方法。在这种情况下,none of these paths need to exist 用于真实和比较归结为您所说的字符串比较。

如果不是这样,它将尝试访问这些路径。 如果它们表示文件系统中的位置,即一个路径是 symbolic link 指向另一个路径 isSameFile() 将 return true。 如果他们都退出但没有导致彼此,它将 return false.

如果其中至少一个不存在于文件系统中,它将抛出异常。

    public static void main(String[] args) throws IOException {
        Path p1 = Paths.get("C:", "a", "b", "c", "test.txt");
        Path p2 = Paths.get("C:",".", ".", ".", "FooBar", "..", "a", "b", "c", "test.txt");

        Path p3 = p1.resolveSibling(Paths.get("copy.txt")); // path that leads to another file in the same folder
        Path copy = Files.copy(p1, p3); // contents of p1 is being coped into p3, path to p3 is returned

        System.out.println("Path 'p1': " + p1);
        System.out.println("Path 'p2`: " + p2);
        System.out.println("Path 'copy': " + copy);
        System.out.println("\np1 and p2 are the same:   " + Files.isSameFile(p1, p2));
        System.out.println("p1 and copy are the same: " + Files.isSameFile(p1, copy));

//        Path p4 = Paths.get("C:", "a", "link.txt");
//        Path symbolicLink = Files.createSymbolicLink(p4, p1);
//        System.out.println("p1 and symbolicLink are the same: " + Files.isSameFile(p1, symbolicLink));
    }

处理符号 link 的行被注释掉了,因为它不能以如此直接的方式完成。 Symbolik links 可能会引入漏洞,系统将不允许 JVM 执行此操作。但是如果我们假设有一个指向 p1 的符号 link 那么 Files.isSameFile(p1, symbolicLink) 将 return true

输出

Path 'p1': C:\a\b\c\test.txt
Path 'p2`: C:\.\.\.\FooBar\..\a\b\c\test.txt
Path 'copy': C:\a\b\c\copy.txt

p1 and p2 are the same:   true
p1 and copy are the same: false

文件内容比较

对于 apart from Files.mismatch() 中的 compare the actual contents 文件 only 可以访问 only 最新的 long-term 支持版本 Java 您可以创建自己的方法查找文件之间的差异:

    public static boolean compareFiles(Path p1, Path p2) throws IOException {
        boolean isSameFile = true;
        try (var input1 = new BufferedInputStream(Files.newInputStream(p1));
             var input2 = new BufferedInputStream(Files.newInputStream(p2))) {
            byte[] buffer1 = new byte[1024];
            byte[] buffer2 = new byte[1024];
            int bytesRead1 = -1;
            int bytesRead2 = -1;
            while (isSameFile  && bytesRead1 != 0 && bytesRead2 != 0) {
                bytesRead1 = input1.readNBytes(buffer1, 0, buffer1.length);
                bytesRead2 = input2.readNBytes(buffer2, 0, buffer2.length);
                if (!Arrays.equals(buffer1, buffer2)) {
                    isSameFile = false;
                }
            }
        }
        return true;
    }

你需要的API调用是Files.mismatch(Path,Path),当两个文件的字节比较相同时returns -1。

boolean identical = Files.mismatch(pathA, pathB) == -1;

如其他评论所述,Files.isSameFile 处理两个不同的路径可能引用文件系统上相同的底层文件的情况。例如,如果我有目录“build”和文件“build.xml”:

Path a = Path.of("build.xml");
Path b = Path.of("build/../build.xml");

Files.isSameFile(a,b)
==> true
a.equals(b)
==> false

还值得注意的是 - 在 JDK17 源代码中 - Files.mismatch 的第一步是 if (Files.isSameFile(a,b)) { return -1; }; 这可能避免需要打开文件进行 byte-wise 比较。