2014-12-09 4 views
37

Как вы напишете java-функцию boolean sameContent(Path file1,Path file2), которая определяет, указывают ли эти два пути на файлы, которые хранят один и тот же контент? Конечно, во-первых, я бы проверял, одинаковы ли размеры файлов. Это необходимое условие для хранения одного и того же содержимого. Но тогда я хотел бы послушать ваши подходы. Если два файла хранятся на одном жестком диске (как и в большинстве моих случаев), это, вероятно, не самый лучший способ перепрыгнуть слишком много раз между этими двумя потоками.Определите, сохраняются ли два файла одного и того же контента

+2

Размер может отличаться, а также для того же содержания. В зависимости от нескольких факторов. Если вы действительно хотите сравнить контент, то простая проверка - сделать контрольную сумму обоих файлов и сравнить их. Вы можете использовать md5 в байтах файлов. Также можно использовать сравнение байтов. –

ответ

2

This должно помочь вам с вашей проблемой:

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); 
    } 
} 
51

Именно то, что FileUtils.contentEquals метод Apache Commons IO делает и АФИ here.

Попробуйте что-то вроде:

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

Это следующие проверки перед тем, как выполнить сравнение:

  • существование как файлы
  • Оба файла, которые передаются должны быть файл тип, а не каталог.
  • Длина в байтах не должна быть одинаковой.
  • Оба - разные файлы, а не одно и то же.
  • Затем сравните содержимое.
+1

Чтобы добавить значение, я обнаружил, что 'FileUtils.contentEqualsIgnoreEOL' может предложить удобство для менее строгих утверждений. – CloudyTrees

14

Если вы не хотите использовать какие-либо внешние библиотеки, а затем просто читать файлы в байтовые массивы и сравнить их (не будет работать заранее Java-7):

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

с помощью Arrays.equals ,

Если файлы большие, то вместо того, чтобы считывать все файлы в массивы, вы должны использовать BufferedInputStream и читать файлы по куску, как описано here.

+1

Я хочу, чтобы моя программа также работала с большими файлами. Это может привести к OutOfMemoryError - если массив требуемого размера не может быть выделен, например, размер файла больше 2 ГБ. Редактировать: Извините, я только что видел ваше замечание о работе с большими файлами. –

+2

Правда. Вот почему я включил ссылку на страницу SO, в которой упоминается использование BufferedInputStream и чтение chunk-by-chunk, а не весь файл. Нет никаких дублирующих ответов, которые уже присутствуют в SO. –

7

Если файлы малы, вы можете считывать их как в память, так и сравнивать массивы байтов.

Если файлы невелики, вы можете либо вычислить хеши их содержимого (например, 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; 
} 
+0

Не можете ли вы просто обернуть входные потоки в 'BufferedInputStream'? Тогда метод будет таким же эффективным, как если бы вы использовали 'read (byte [])', но без сложности, не так ли? – aioobe

+0

@aioobe Да, мы могли бы. Причина, по которой я использовал байтовое сравнение, заключается в том, что метод 'read (byte [])' не гарантированно полностью считывает переданный массив байтов (javadoc говорит _ "он читает до' bytes.length' _ _) , Если источником базового потока является файл, текущие реализации будут читать полный массив, но для этого нет никакой гарантии. И код, который должным образом обрабатывает чтение без полного массива, будет более сложным и привлечет внимание из принципа, который пытается показать мой snipplet кода. – icza

+0

Я понимаю это, но то, что я пытаюсь сказать, заключается в том, что вы избегаете * без * этой сложности, используя «BufferedInputStream» (пока еще достигаете эффективности). – aioobe

1

У меня был сценарий, в котором я должен был сравнить любые два файла (видео/изображения/что угодно). Я пошел вперед, вычисляя md5 для двух файлов и сравнивая их. Он работал нормально.

import java.io.FileInputStream; 
import java.io.FileNotFoundException; 
import java.io.IOException; 
import java.security.MessageDigest; 
import java.security.NoSuchAlgorithmException; 

public class MD5EncoderUtility { 
    public String encodeToMd5(String filePath) { 

     MessageDigest md = null; 
     try { 
      md = MessageDigest.getInstance("SHA1"); 
     } catch (NoSuchAlgorithmException e1) { 
      e1.printStackTrace(); 
     } 
     FileInputStream fis = null; 
     try { 
      fis = new FileInputStream(filePath); 
     } catch (FileNotFoundException e) { 
      e.printStackTrace(); 
     } 
     byte[] dataBytes = new byte[1024]; 

     int nread = 0; 

     try { 
      while ((nread = fis.read(dataBytes)) != -1) { 
       md.update(dataBytes, 0, nread); 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

     byte[] mdbytes = md.digest(); 

     // convert the byte to hex format 
     StringBuffer sb = new StringBuffer(""); 
     for (int i = 0; i < mdbytes.length; i++) { 
      sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1)); 
     } 

     System.out.println("Digest(in hex format):: " + sb.toString()); 
     return sb.toString(); 
    } 
} 

public class FileCompareUtility { 
    MD5EncoderUtility md5EncoderUtil = new MD5EncoderUtility(); 

    public boolean compare2Files(String actualFilePath, String expectedFilePath) { 
     if ((md5EncoderUtil.encodeToMd5(actualFilePath)).equals(md5EncoderUtil.encodeToMd5(expectedFilePath))) { 
      System.out.println("The files- "+actualFilePath+" and "+expectedFilePath+" are same"); 
      return true; 
     } else { 
      System.out.println("The files- "+actualFilePath+" and "+expectedFilePath+" are NOT same"); 
      return false; 
     } 
    } 

} 

И можно назвать так:

 String actualFileComparePath = "D:/Videos/test.mp4"; 
     String expectedFileComparePath = "D:/Videos/test2.mp4"; 

// This will compare any types of files- here video.Eg-Video 
     fileCompareUtil.compare2Files(actualFileComparePath, expectedFileComparePath); 
+3

Это крайне неэффективно –

+2

@ AndyBrown Было бы здорово, если бы вы предложили мне эффективный способ сравнить любые два типа файлов (видео, изображения, txt и т. Д.). Заранее спасибо! –

+0

Поскольку вы уже читаете весь поток байтов, почему бы просто не проверить байты байтом? – Panayotis

Смежные вопросы