LINUX.ORG.RU

Найти общий кусок для двух файлов


0

1

Привет всем!

Вот столкнулся с такой задачей. Есть два файла. Первый файл больше другого. Где-то в недрах файла 2 есть большой кусок из файла 1. Неизвестно какой именно кусок, неизвестно какой он величины. Нужно найти начальную и конечную позиции этого куска в файле 2.

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

Ну и если поможет, немного конкретики: Файл1 = 5 мб, файл2 = 3 мб. Оба файла текстовые.

Пока вот сижу ищу визуально, но чую, это затянется. Как быстрее? Заранее спасибо!

P.S. Добавлю. В файле - лог работы компа. Т.е. с точки зрения человека, текст бессвязный, искать визуально очень сложно.

★★★★★

Последнее исправление: hibou (всего исправлений: 1)

Да, еще, о diff подумал в первую очередь. Но она ищет построчно. А тут большой фрагмент, причем длина неизвестна.

hibou ★★★★★
() автор топика

Сначала для строк посчитай хэш-суммы. (Чтобы сравнивать не сами строки, что долго, а только пару чисел, что быстро.) Ну а потом тупо проходишься циклом for-for для сравнения всех строк между собой и поиска одинаковых участков. Каждый совпадающий участок выводи в отдельный файл. Потом при помощи wc -l посмотришь, какой файл имеет наибольшее число строк — профит.

geekless ★★
()
Ответ на: комментарий от hibou

Чорт! Тут еще и дифф из соляры блин! Он не все опции умеет.

hibou ★★★★★
() автор топика

А comm не подойдёт?
Только там требуется, чтобы оба файла предварительно отсортированы были.

DeVliegendeHollander ★★
()
Ответ на: комментарий от geekless

Благодрю. Вообще грустно засесть на этом. Это вообще побочное дело, сателлит. :)

hibou ★★★★★
() автор топика
Ответ на: комментарий от hibou

Есть docdiff с выводом в html, там наглядно все будет видно. Не знаю если подходит для твоих целей, но посмотри на всякий случай.

У меня часто бывают на верстку брошюры как у тебя (большие фрагменты повторяются). Раньше пользовался diff-ом, сейчас использую docdiff - удобнее. Он может сравнивать и построчно, и посимвольно и еще как-то. В общем, в моем случае он действительно удобнее обычного diff-а. Посмотри, может подойдет.

gnunixon ★★★
()
Ответ на: комментарий от gnunixon

Тут к сожалению солярис. Ынтырпрайс.

hibou ★★★★★
() автор топика
Ответ на: комментарий от geekless

можно кстати без вложенных for-for, и сравнения всех строк между собой. Достаточно ассоциации строка->индексы_во_втором_файле и однократного прохода по первому файлу (с небольшой рекурсией связанной с дубликатами строк). Да и на выходе можно получить сразу максимальный кусок - все необходимые данные для этого есть. Но это так - лирическое отступление.

в бытность работы под Солярис и HP-UX на них отдельно ставили GNU text-utils именно из-за куцеватости оригинальных.

А вообще если это «лог работы компа» то там есть метки времени, событий, систем. За время обсуждения темы, любой админ мог найти искомый фрагмент, принять пива/кофе, разрулить проблему, завалить и заново поднять систему :)

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

За время обсуждения темы, любой админ мог найти искомый фрагмент, принять пива/кофе, разрулить проблему, завалить и заново поднять систему

Что hibou наверняка и сделал.

geekless ★★
()

раз это логи, то, вероятно, там есть поле дата/время и значит все строки каждого файла можно считать уникальными; тогда можно погрепать один файл «об другой» с выводом номеров строк исходного файла:

$ grep -n -f файл1 файл2
потом останется найти самый большой диапазон номеров строк без дыр

anonymous
()
Ответ на: комментарий от anonymous

Хех... не подумал вовремя. Нет, конкретно эти логи без тайм-штампов. Чтобы были с тайм-штампами, нужно было снимать их по-другому. Логи вообще бинарные, но они снимаются в текстовый формат. И вот я их снял без тайм-штампов. Переснять пока не смогу. Комп уже за другим разрабом. Позже наверно так и сниму (с отметками времени).

hibou ★★★★★
() автор топика
Ответ на: комментарий от hibou

т.е. задача еще актуальна? а то тут уже предположили, как ты пиво пьешь :)

Нет, конкретно эти логи без тайм-штампов.

ну, здесь главное, чтобы стоки уникальными оказались, может это чем-нибудь другим обеспечивается, PID'ами, например...

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

anonymous
()
Ответ на: комментарий от anonymous

Актуальна. Но меня переключили на другую задачу. Ту я должен отправить в саспенд. :)

Позже к ней вернусь, правда когда - без понятия. Логи у меня есть, я их скинул к себе на машину. Но той тестовой машины, с которой логи, у меня теперь нет.

hibou ★★★★★
() автор топика

http://en.wikipedia.org/wiki/Longest_common_substring_problem Файлы можно расценивать как две строки (в к-рых, естественно,есть символы \n) и задача представляет собой поиск наибольшей общей подстроки. На википедии сложность алгоритма по времени там оценена в O(mn), по памяти - O(min(m, n)). Вроде более-менее.

anonymous
()

чё тут думать-то

cat mystrstr.c:

#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <malloc.h>

int main(int argc, char ** argv)
{
        if (argc!=3)
                return 1;
        FILE * haystackfile = fopen(argv[1], "rt");
        FILE * needlefile = fopen(argv[2], "rt");
        struct stat haystackstat;
        struct stat needlestat;
        if (!haystackfile) { fputs("fopen haystackfile failed", stderr); return 2; }
        if (!needlefile) { fputs("fopen needlefile failed", stderr); return 2; }

        int haystack_stat_res = fstat(fileno(haystackfile), &haystackstat);
        int needle_stat_res = fstat(fileno(needlefile), &needlestat);
        if (haystack_stat_res) { fputs("fstat haystack_stat_res failed", stderr); return 2; }
        if (needle_stat_res) { fputs("fstat needle_stat_res failed", stderr); return 2; }

        char * haystack = malloc(haystackstat.st_size+1);
        char * needle = malloc(needlestat.st_size+1);
        if (!haystack) { fputs("malloc haystack failed", stderr); return 2; }
        if (!needle) { fputs("malloc needle failed", stderr); return 2; }

        int haystack_bytes = fread(haystack, 1, haystackstat.st_size, haystackfile);
        int needle_bytes = fread(needle, 1, needlestat.st_size, needlefile);

        fprintf(stderr, "haystack_bytes=%d, needle_bytes=%d\n", haystack_bytes, needle_bytes);

        fclose(haystackfile);
        fclose(needlefile);

        haystack[haystackstat.st_size] = '\0';
        needle[needlestat.st_size] = '\0';

        char * found = strstr(haystack, needle);

        if (found)
                printf("Found at position %d\n", found-haystack);
        else
                puts("Not found");

}
┌[legolegs@battlehummer ~/programing] :)
└> cc mystrstr.c -o mystrstr
┌[legolegs@battlehummer ~/programing] :)
└> dd if=/dev/urandom count=3k bs=1k | hexdump > needle.hex3072+0 записей считано
3072+0 записей написано
 скопировано 3145728 байт (3,1 MB), 0,392286 c, 8,0 MB/c
┌[legolegs@battlehummer ~/programing] :)
└> (dd if=/dev/urandom count=1000 bs=1k | hexdump; cat needle.hex; dd if=/dev/urandom count=1000 bs=1k | hexdump) > haystack.hex 
1000+0 записей считано
1000+0 записей написано
 скопировано 1024000 байт (1,0 MB), 0,122505 c, 8,4 MB/c
1000+0 записей считано
1000+0 записей написано
 скопировано 1024000 байт (1,0 MB), 0,123013 c, 8,3 MB/c
┌[legolegs@battlehummer ~/programing] :)
└> ll haystack.hex needle.hex 
-rw-r----- 1 legolegs legolegs  15M апр.   8 13:47 haystack.hex
-rw-r----- 1 legolegs legolegs 9,1M апр.   8 13:47 needle.hex
┌[legolegs@battlehummer ~/programing] :)
└> time ./mystrstr haystack.hex needle.hex
haystack_bytes=15581208, needle_bytes=9437192
Found at position 3072008

real    0m0.088s
user    0m0.065s
sys     0m0.022s

legolegs ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.