判断两个文件是否存储相同的内容

问题描述 投票:0回答:10

如何编写一个java函数

boolean sameContent(Path file1,Path file2)
来确定两个给定路径是否指向存储相同内容的文件?当然,首先,我会检查文件大小是否相同。这是存储相同内容的必要条件。但我想听听你的方法。如果这两个文件存储在同一个硬盘驱动器上(就像在我的大多数情况下),这可能不是在两个流之间跳转太多次的最佳方法。

java file comparison
10个回答
106
投票

Apache commons IO 的

FileUtils.contentEquals
方法和 api 的具体用途是这里

尝试类似:

File file1 = new File("file1.txt");
File file2 = new File("file2.txt");
boolean isTwoEqual = FileUtils.contentEquals(file1, file2);

在实际进行比较之前,它会执行以下检查:

  • 两个文件都存在
  • 传递的两个文件都是文件类型而不是目录。
  • 字节长度应该相同。
  • 两者都是不同的文件,而不是同一个。
  • 然后比较内容。

38
投票

如果您不想使用任何外部库,则只需将文件读入字节数组并进行比较(在 Java-7 之前不起作用):

byte[] f1 = Files.readAllBytes(file1);
byte[] f2 = Files.readAllBytes(file2);

通过使用 Arrays.equals

如果文件很大,那么您应该使用

BufferedInputStream
并按照此处的说明逐块读取文件,而不是将整个文件读入数组。


22
投票

自 Java 12 起,有方法 Files.mismatch 如果文件内容不存在不匹配,则返回

-1
。因此该函数如下所示:

private static boolean sameContent(Path file1, Path file2) throws IOException {
    return Files.mismatch(file1, file2) == -1;
}

14
投票

如果文件很小,可以将两者读入内存并比较字节数组。

如果文件不小,您可以依次计算其内容的哈希值(例如 MD5 或 SHA-1)并比较哈希值(但这仍然会留下很小的错误机会),或者您可以比较他们的内容,但为此你仍然必须交替阅读流。

这是一个例子:

boolean sameContent(Path file1, Path file2) throws IOException {
    final long size = Files.size(file1);
    if (size != Files.size(file2))
        return false;

    if (size < 4096)
        return Arrays.equals(Files.readAllBytes(file1), Files.readAllBytes(file2));

    try (InputStream is1 = Files.newInputStream(file1);
         InputStream is2 = Files.newInputStream(file2)) {
        // Compare byte-by-byte.
        // Note that this can be sped up drastically by reading large chunks
        // (e.g. 16 KBs) but care must be taken as InputStream.read(byte[])
        // does not neccessarily read a whole array!
        int data;
        while ((data = is1.read()) != -1)
            if (data != is2.read())
                return false;
    }

    return true;
}

7
投票

应该可以帮助您解决问题:

package test;

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;

public class CompareFileContents {

    public static void main(String[] args) throws IOException {

        File file1 = new File("test1.txt");
        File file2 = new File("test2.txt");
        File file3 = new File("test3.txt");

        boolean compare1and2 = FileUtils.contentEquals(file1, file2);
        boolean compare2and3 = FileUtils.contentEquals(file2, file3);
        boolean compare1and3 = FileUtils.contentEquals(file1, file3);

        System.out.println("Are test1.txt and test2.txt the same? " + compare1and2);
        System.out.println("Are test2.txt and test3.txt the same? " + compare2and3);
        System.out.println("Are test1.txt and test3.txt the same? " + compare1and3);
    }
}

7
投票

如果是单元测试,那么AssertJ提供了一个名为hasSameContentAs的方法。一个例子:

Assertions.assertThat(file1).hasSameContentAs(file2)

2
投票

我知道我在这方面已经很晚了,但是如果您想使用直接的 Java API 并且没有第三方依赖项,那么内存映射 IO 是一种非常简单的方法。只需几次调用即可打开文件、映射它们,然后使用

ByteBuffer.equals(Object)
来比较文件。

如果您希望特定文件很大,那么这可能会给您带来最佳性能,因为您将大部分 IO 工作转移到操作系统以及 JVM 的其他高度优化位上(假设您使用的是不错的 JVM)。

直接从 FileChannel JavaDoc

对于大多数操作系统来说,将文件映射到内存比通过通常的读写方法读取或写入几十KB的数据要昂贵。从性能的角度来看,通常只值得将相对较大的文件映射到内存中。

import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;


public class MemoryMappedCompare {

    public static boolean areFilesIdenticalMemoryMapped(final Path a, final Path b) throws IOException {
        try (final FileChannel fca = FileChannel.open(a, StandardOpenOption.READ);
             final FileChannel fcb = FileChannel.open(b, StandardOpenOption.READ)) {
            final MappedByteBuffer mbba = fca.map(FileChannel.MapMode.READ_ONLY, 0, fca.size());
            final MappedByteBuffer mbbb = fcb.map(FileChannel.MapMode.READ_ONLY, 0, fcb.size());
            return mbba.equals(mbbb);
        }
    }
}


1
投票

它> = JR6兼容,无库,并且不要一次阅读所有内容。

public static boolean sameFile(File a, File b) {
    if (a == null || b == null) {
        return false;
    }

    if (a.getAbsolutePath().equals(b.getAbsolutePath())) {
        return true;
    }

    if (!a.exists() || !b.exists()) {
        return false;
    }
    if (a.length() != b.length()) {
        return false;
    }
    boolean eq = true;
    
    FileChannel channelA;
    FileChannel channelB;
    try {
        channelA = new RandomAccessFile(a, "r").getChannel();
        channelB = new RandomAccessFile(b, "r").getChannel();

        long channelsSize = channelA.size();
        ByteBuffer buff1 = channelA.map(FileChannel.MapMode.READ_ONLY, 0, channelsSize);
        ByteBuffer buff2 = channelB.map(FileChannel.MapMode.READ_ONLY, 0, channelsSize);
        for (int i = 0; i < channelsSize; i++) {
            if (buff1.get(i) != buff2.get(i)) {
                eq = false;
                break;
            }
        }
    } catch (FileNotFoundException ex) {
        Logger.getLogger(HotUtils.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IOException ex) {
        Logger.getLogger(HotUtils.class.getName()).log(Level.SEVERE, null, ex);
    }
    return eq; 
}

0
投票

校验和检查 rene m。在评论中告诉你是最好的方法


-3
投票
package test;  

      import org.junit.jupiter.api.Test;

      import java.io.IOException;
      import java.nio.file.FileSystems;
      import java.nio.file.Files;
      import java.nio.file.Path;

import static org.junit.Assert.assertEquals;

public class CSVResultDIfference {

   @Test
   public void csvDifference() throws IOException {
       Path file_F = FileSystems.getDefault().getPath("C:\\Projekts\\csvTestX", "yolo2.csv");
       long size_F = Files.size(file_F);
       Path file_I = FileSystems.getDefault().getPath("C:\\Projekts\\csvTestZ", "yolo2.csv");
       long size_I = Files.size(file_I);
       assertEquals(size_F, size_I);

   }
}

它对我有用:)

© www.soinside.com 2019 - 2024. All rights reserved.