Java:如何检查两个二进制文件是否相同?

17

在单元测试中,检查二进制文件A和B是否相等的最简单方法是什么?


4
MD5或SHA1可能适用于此。 - donfuxx
嗨,如果您使用的是Linux操作系统,请使用“md5sum”命令。https://help.ubuntu.com/community/HowToMD5SUM - sprabhakaran
3
第三方库是否公平竞争?Guava有Files.equal。如果不必要,没有真正的理由去烦恼哈希;它只会更低效。 - Louis Wasserman
@LouisWasserman 是的。请将您的评论提交为答案(Guava的Files.equal),我会接受它。 - Glory to Russia
7个回答

13

第三方库是否可以使用?Guava拥有Files.equal(File, File)函数。如果没有必要,不必费力计算哈希值;这只会降低效率。


1
该API带有@Beta注释。 - coverboy

7

一种方法是逐字节从每个文件中读取并比较。即使使用Md5和Sha1等哈希算法,也必须读取所有字节,因此计算哈希值是不必要的额外工作。

if (file1.length() != file2.length()) {
    return false;
}
    
 try( InputStream in1 = new BufferedInputStream(new FileInputStream(file1));
    InputStream in2 = new BufferedInputStream(new FileInputStream(file2));
 ) {

      int value1, value2;
      do {
           //since we're buffered, read() isn't expensive
           value1 = in1.read();
           value2 = in2.read();
           if(value1 != value2) {
               return false;
           }
      } while(value1 >= 0);
     
 // since we already checked that the file sizes are equal 
 // if we're here we reached the end of both files without a mismatch
 return true;
}

4
这取决于您需要比较多少次。如果您有文件A,并且需要将许多文件(B1,...,Bn)与其进行比较,则使用计算哈希值可能更有效。这样一来,每次测试时就不必遍历整个A的字节。您可以在单元测试中放置哈希值并检查其是否匹配(只需确保在文件更改时更改哈希值)。 - yby

4

逐块读取文件并进行比较:

static boolean binaryDiff(File a, File b) throws IOException {
    if(a.length() != b.length()){
        return false;
    }
    
    final int BLOCK_SIZE = 128;
    InputStream aStream = new FileInputStream(a);
    InputStream bStream = new FileInputStream(b);
    byte[] aBuffer = new byte[BLOCK_SIZE];
    byte[] bBuffer = new byte[BLOCK_SIZE];
    do {
        int aByteCount = aStream.read(aBuffer, 0, BLOCK_SIZE);
        bStream.read(bBuffer, 0, BLOCK_SIZE);
        if (!Arrays.equals(aBuffer, bBuffer)) {
            return false;
        }
    }
    while(aByteCount < 0);
    return true;
}

4

4

如果你想避免依赖关系,你可以使用Files.readAllBytes和Assert.assertArrayEquals来完成这个任务。

Assert.assertArrayEquals("Binary files differ", 
    Files.readAllBytes(Paths.get(expectedBinaryFile)), 
    Files.readAllBytes(Paths.get(actualBinaryFile)));

注意:这将读取整个文件,因此在处理大文件时可能不够高效。

2
自Java 12开始,您还可以使用Files.mismatch方法JavaDoc。如果文件相同,则它将返回-1L

1

我在单元测试中也需要做同样的事情,因此我使用SHA1哈希来实现,为了避免计算哈希值,我首先检查文件大小是否相等。以下是我的尝试:

public class SHA1Compare {
    private static final int CHUNK_SIZE = 4096;

    public void assertEqualsSHA1(String expectedPath, String actualPath) throws IOException, NoSuchAlgorithmException {
        File expectedFile = new File(expectedPath);
        File actualFile = new File(actualPath);
        Assert.assertEquals(expectedFile.length(), actualFile.length());
        try (FileInputStream fisExpected = new FileInputStream(actualFile);
                FileInputStream fisActual = new FileInputStream(expectedFile)) {
            Assert.assertEquals(makeMessageDigest(fisExpected), 
                    makeMessageDigest(fisActual));
        }
    }

    public String makeMessageDigest(InputStream is) throws NoSuchAlgorithmException, IOException {
        byte[] data = new byte[CHUNK_SIZE];
        MessageDigest md = MessageDigest.getInstance("SHA1");
        int bytesRead = 0;
        while(-1 != (bytesRead = is.read(data, 0, CHUNK_SIZE))) {
            md.update(data, 0, bytesRead);
        }
        return toHexString(md.digest());
    }

    private String toHexString(byte[] digest) {
        StringBuilder sha1HexString = new StringBuilder();
        for(int i = 0; i < digest.length; i++) {
            sha1HexString.append(String.format("%1$02x", Byte.valueOf(digest[i])));
        }
        return sha1HexString.toString();
    }
}

4
计算哈希比逐字节比较两个文件需要更多计算资源。 - Charlie
1
@Charlie,没错,如果期望的哈希值已经被计算过并存储在某个变量中供测试使用,那么计算哈希值会更加合适,因此只需要计算实际文件的哈希值即可。 - A4L

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接