Какой самый быстрый способ сравнить два больших файла в Java

В настоящее время у нас есть ночной запуск автоматизации, который сравнивает результирующий тестовый файл, созданный нашим программным обеспечением, и базовый файл. Это сравнение выполняется несколько раз, и файлы имеют большой размер. Сравнение файлов является узким местом в нашей автоматизации тестирования.

Сравнение файлов в настоящее время выполняется путем буферизации построчного сравнения.

Я думал о сравнении контрольной суммы двух файлов (затем выполняя построчную проверку, если контрольные суммы не совпадают). Это лучший подход? Есть ли публичная библиотека, которую кто-то хотел бы предложить?

Спасибо


person Huckleberry    schedule 30.11.2017    source источник
comment
Файлы должны быть одинаковыми или разными? Вы должны видеть различия?   -  person AntonH    schedule 30.11.2017
comment
и, пожалуйста, определите большой. Сравнение контрольных сумм - лучший способ, если они уже предварительно вычислены, если вам нужно вычислить их сначала, это, безусловно, один из наименее производительных способов.   -  person luk2302    schedule 30.11.2017
comment
Вы можете начать с проверки размера файла, если они не совпадают, они уже разные, также контрольная сумма всего файла - хороший способ проверить равенство   -  person Marcos Vasconcelos    schedule 30.11.2017
comment
Построчное, вероятно, означает преобразование байтов файла в символы в соответствии с некоторой кодировкой символов, поиск новых строк, создание строк, представляющих строки, которые должны быть удалены сборщиком мусора, когда строка больше не нужна, и т. д., поэтому переключение на более низкий уровень , ввод-вывод байтового буфера может улучшить скорость, но сначала используйте профилировщик, чтобы выяснить, является ли это узким местом...   -  person Ralf Kleberhoff    schedule 30.11.2017
comment
Файлы должны быть одинаковыми, мы ищем и помечаем те, которые отличаются. Мы утверждаем, что они одинаковы.   -  person Huckleberry    schedule 30.11.2017
comment
Размер файла составляет около 192 КБ, что в ретроспективе не так уж и много, но их сравнение является узким местом в нашей автоматизации тестирования.   -  person Huckleberry    schedule 30.11.2017
comment
Возможный дубликат Определить, хранят ли два файла один и тот же контент   -  person NickL    schedule 30.11.2017
comment
ИМХО для файлов, которые должны быть равны (байт за байтом) и размером до нескольких сотен килобайт, я бы сначала сравнил их размеры, и если они равны, просто выделите двухбайтовые массивы нужного размера, полностью прочитайте файлы и просто сравните байтовые массивы. Для больших файлов я бы выделил два блочных буфера размером 128 КБ и читал файлы по блокам, пока либо блоки не различались, либо файлы не были полностью прочитаны.   -  person Thomas Kläger    schedule 30.11.2017


Ответы (1)


Достаточно ли 10 мс для сравнения двух файлов размером 260 КБ? (на ноутбуке с Windows)

Если это так, вы можете использовать java.security.DigestInputStream для вычисления и сравнения Hash.

Конечно делаешь, проверяй длину файлов перед этим. Если проблема связана со многими файлами, которые необходимо сравнить, рассмотрите возможность использования параллельных потоков для сравнения каждой пары.

Образец кода:

public static void main(String[] args) {

    try {
        File file1 = new File("D:\\tmp\\tests\\logs\\test.log");
        File file2 = new File("D:\\tmp\\tests\\logs\\test-cp.log");

        if (!file1.exists() || !file2.exists()) {
            System.out.println("One of the file not found.");
            return;
        }
        if (file1.length() != file2.length()) {
            System.out
                    .println("Files are not identical - not equal length.");
            return;
        }

        long f1Length = file1.length();
        long f2Length = file2.length();

        System.out.println("Check Digest method:");
        FileInputStream fis1 = new FileInputStream(file1);
        DigestInputStream dgStream1 = new DigestInputStream(fis1,
                MessageDigest.getInstance("MD5"));
        FileInputStream fis2 = new FileInputStream(file2);
        DigestInputStream dgStream2 = new DigestInputStream(fis2,
                MessageDigest.getInstance("MD5"));
        // most expensive is dgStream1.getMessageDigest() so do it only at last read
        dgStream1.on(false);
        dgStream2.on(false);

        long f1ReadTotal = 0;
        long f2ReadTotal = 0;

        long start = System.nanoTime();

        int read = 0;
        byte[] buff = new byte[1024 * 128];
        do {
            if ((f1Length - f1ReadTotal) < (1024 * 128)) {
                // last read 
                dgStream1.on(true);
            }
            read = dgStream1.read(buff);
            f1ReadTotal += read > 0 ? read : 0;
        } while (read > 0);

        read = 0;
        do {
            if ((f2Length - f2ReadTotal) < (1024 * 128)) {
                // last read
                dgStream2.on(true);
            }
            read = dgStream2.read(buff);
            f2ReadTotal += read > 0 ? read : 0;
        } while (read > 0);

        long runTime = System.nanoTime() - start;
        if (Arrays.equals(dgStream1.getMessageDigest().digest(), dgStream2
                .getMessageDigest().digest())) {
            System.out.println("Files are identical. completed in "
                    + (runTime / 1000000) + " ms. [" + runTime + " ns.]");
        } else {
            System.out.println("Files are not identical. completed in "
                    + (runTime / 1000000) + " ms. [" + runTime + " ns.]");
        }

        fis1.close();
        fis2.close();

    } catch (Exception e) {
        e.printStackTrace();
    }

}

Главное, что getMessageDigest() - это самая затратная по времени операция, поэтому сделайте это один раз, наконец, прочитав.

Кстати: код — это просто идея. Реальный код должен быть более осторожным, особенно в отношении «последнего чтения», и определенно может быть более оптимальным.

person Vadim    schedule 30.11.2017