您将如何编写Java函数boolean sameContent(Path file1,Path file2)
来确定两个给定路径是否指向存储相同内容的文件?当然,首先,我将检查文件大小是否相同。这是存储相同内容的必要条件。但是,我想听听您的方法。如果两个文件存储在同一硬盘上(就像我大多数情况一样),这可能不是在两个流之间跳转太多次的最佳方法。
Answers:
究竟FileUtils.contentEquals
Apache Commons IO和api的方法在这里。
尝试类似:
File file1 = new File("file1.txt");
File file2 = new File("file2.txt");
boolean isTwoEqual = FileUtils.contentEquals(file1, file2);
在实际进行比较之前,它将进行以下检查:
FileUtils.contentEqualsIgnoreEOL
可以为不太严格的断言提供方便。
如果您不想使用任何外部库,则只需将文件读入字节数组并进行比较(在Java-7之前不起作用):
byte[] f1 = Files.readAllBytes(file1);
byte[] f2 = Files.readAllBytes(file2);
通过使用Arrays.equals。
如果文件很大,则应按此处所述BufferedInputStream
逐块使用和读取文件,而不是将整个文件读入数组。
如果文件很小,则可以将它们都读入内存并比较字节数组。
如果文件不小,则可以一个接一个地计算其内容的哈希值(例如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;
}
BufferedInputStream
吗?然后,该方法将像您使用的方法一样高效,read(byte[])
但是没有复杂性,对吗?
read(byte[])
不能保证该方法完全读取传递的字节数组(javadoc说“它最多读取bytes.length
”)。如果基础流的源是文件,则当前的实现将读取完整的数组,但对此不做任何保证。正确处理非完整数组读取的代码会更复杂,并且会引起我的代码片段试图展示的原理的注意。
BufferedInputStream
(同时仍然可以提高效率)。
BufferedInputStream.read(byte[] b, int off, int len)
确实声明它试图读取完整的数组。并且尽管BufferedInputStream
没有覆盖FilterInputStream.read(byte[] b)
,FilterInputStream.read(byte[] b)
状态的javadoc声明实现调用了read(b, 0, b.length)
该方法(如果是a,BufferedInputStream
则将调用该BufferedInputStream.read(byte[] b, int off, int len)
方法)。
Files.newInputStream(file1)
为new BufferedInputStream(Files.newInputStream(file1))
您的is1.read()
电话将对应于简单的数组访问(在大多数情况下),整个工作read(byte[] ...)
将在幕后进行。因此,我建议您通过包装输入流BufferedInputStreams
并在如何增加速度方面添加注释,以增加额外的复杂性,从而改善您的答案。
从Java 12开始,存在方法Files.mismatch,-1
如果文件内容没有不匹配,则返回该方法。因此该函数如下所示:
private static boolean sameContent(Path file1, Path file2) throws IOException {
return Files.mismatch(file1, file2) == -1;
}
这应该可以帮助您解决问题:
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);
}
}
如果用于单元测试,则AssertJ提供一个名为hasSameContentAs的方法。一个例子:
Assertions.assertThat(file1).hasSameContentAs(file2)
hasSameTextualContentAs
我知道我要参加这个聚会还很晚,但是如果您想使用直接的Java API并且没有第三方依赖关系,则内存映射IO是一种非常简单的方法。只需几次调用即可打开文件,映射它们,然后进行比较ByteBuffer.equals(Object)
以比较文件。
如果您希望特定文件很大,那么这可能会给您带来最佳性能,因为您将大部分IO开销转移到了OS以及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);
}
}
}
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);
}
}
它对我有用:)