你将如何编写一个 Java 函数 boolean sameContent(Path file1,Path file2)
,用于确定这两个给定的路径指向存储相同内容的文件?当然,首先我会检查文件大小是否相同。这是存储相同内容的必要条件。但是,我想听听你的方法。如果这两个文件存储在同一硬盘驱动器上(像在我大多数情况下一样),可能不是在两个流之间跳转太多次的最佳方式。
你将如何编写一个 Java 函数 boolean sameContent(Path file1,Path file2)
,用于确定这两个给定的路径指向存储相同内容的文件?当然,首先我会检查文件大小是否相同。这是存储相同内容的必要条件。但是,我想听听你的方法。如果这两个文件存储在同一硬盘驱动器上(像在我大多数情况下一样),可能不是在两个流之间跳转太多次的最佳方式。
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
可以为宽松的断言提供方便。 - CloudyTrees如果您不想使用任何外部库,那么只需将文件读入字节数组并进行比较即可(在Java 7之前不起作用):
byte[] f1 = Files.readAllBytes(file1);
byte[] f2 = Files.readAllBytes(file2);
通过使用 Arrays.equals 方法进行比较。
如果文件很大,那么你应该使用 BufferedInputStream
,并按照这里所述,将文件分块读取而不是将整个文件读入数组中。
-1
。因此,该函数看起来应该如下:private static boolean sameContent(Path file1, Path file2) throws IOException {
return Files.mismatch(file1, file2) == -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[])
一样高效,但没有那么复杂,对吧? - aiooberead(byte[])
方法不能保证完全读取传递的字节数组(javadoc说“它最多读取bytes.length
”)。如果基础流的源是文件,则当前实现将读取整个数组,但不能保证此操作。而且,正确处理非完整数组读取的代码会更加复杂,并且会吸引注意力,使我的代码片段所展示的原则不明显。 - iczaBufferedInputStream.read(byte[] b, int off, int len)
的 javadoc 确实说明它尝试读取整个数组。虽然 BufferedInputStream
没有覆盖 FilterInputStream.read(byte[] b)
,但是 FilterInputStream.read(byte[] b)
的 javadoc 表明实现会调用 read(b, 0, b.length)
,而在 BufferedInputStream
的情况下,这将调用 BufferedInputStream.read(byte[] b, int off, int len)
方法。 - iczaFiles.newInputStream(file1)
更改为new BufferedInputStream(Files.newInputStream(file1))
,您的is1.read()
调用将对应于简单的数组访问(在大多数情况下),整个read(byte [] ...)
操作将在幕后处理。因此,我建议您通过在BufferedInputStreams
中包装输入流并删除有关如何以牺牲额外复杂性来加速的注释来改进您的答案。 - aioobe这个链接可以帮助您解决问题:
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
。 - HamsterhasSameContentAs
method has been deprecated, please use instead hasSameBinaryContentAs:`assertThat(file1).hasSameBinaryContentAs(file2);`
- Anyul Rivas我知道我在这个问题上来得很晚,但如果你想使用纯Java API和没有第三方依赖项,内存映射IO是一个非常简单的方法。只需要几个调用来打开文件,映射它们,然后使用ByteBuffer.equals(Object)
比较文件。
如果您希望特定文件是大文件,那么这可能会为您提供最佳性能,因为您将大部分IO工作转移到操作系统和JVM的高度优化位(假设您正在使用良好的JVM)。
直接从FileChannel JavaDoc中引用:
对于大多数操作系统,将文件映射到内存中比通过通常的读取和写入方法读取或写入几十千字节的数据更加昂贵。从性能的角度来看,通常只有将相对较大的文件映射到内存中才值得。
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);
}
}
}
map()
函数的位置和长度字段,以块的方式比较文件。 - localhost它兼容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;
}
Rene M. 在评论中提到的校验和检查是最好的方法。
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);
}
}
这对我有用 :)