Разница двух больших файлов

У меня есть "test1.csv", и он содержит

200,400,600,800
100,300,500,700
50,25,125,310

и test2.csv, и он содержит

100,4,2,1,7
200,400,600,800
21,22,23,24,25
50,25,125,310
50,25,700,5

сейчас

diff test2.csv test1.csv > result.csv

отличается от

diff test1.csv test2.csv > result.csv

Я не знаю, какой правильный порядок, но я хочу что-то еще, обе команды выше будут выводить что-то вроде

2 > 100,4,2,1,7
   3 2,3c3,5
   4 < 100,300,500,700
   5 < 50,25,125,310
   6 \ No newline at end of file
   7 ---
   8 > 21,22,23,24,25
   9 > 50,25,125,310

Я хочу вывести только разницу, поэтому results.csv должен выглядеть следующим образом

100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Я старался diff -q а также diff -s но они не добились цели. Порядок не имеет значения, важно то, что я хочу видеть только разницу, ни> ни <, ни пробел.

grep -FvF сделал трюк с меньшими файлами, а не с большими

Первый файл содержит более 5 миллионов строк, второй файл содержит 1300.

в результате файл result.csv должен содержать ~4 998 700 строк

Я тоже пробовал grep -F -x -v -f который не работал.

5 ответов

Решение

Похоже, работа для comm:

$ comm -3 <(sort test1.csv) <(sort test2.csv)
100,300,500,700
    100,4,2,1,7
    21,22,23,24,25
    50,25,700,5

Как объяснено в man comm:

   -1     suppress column 1 (lines unique to FILE1)

   -2     suppress column 2 (lines unique to FILE2)

   -3     suppress column 3 (lines that appear in both files)

Итак -3 означает, что будут напечатаны только строки, уникальные для одного из файлов. Однако они имеют отступ в соответствии с тем, в каком файле они были найдены. Чтобы удалить вкладку, используйте:

$ comm -3 <(sort test1.csv) <(sort test2.csv) | tr -d '\t'
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

В этом случае вам даже не нужно сортировать файлы, и вы можете упростить приведенное выше:

comm -3 test1.csv test2.csv | tr -d '\t' > difference.csv

С помощью grep с bash процесс замены:

$ cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv)
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Сохранить результат как results.csv:

cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv) >results.csv
  • <() это bash шаблон процесса замены

  • grep -vFf test2.csv test1.csv найдет линии, уникальные только для test1.csv

  • grep -vFf test1.csv test2.csv найдет линии, уникальные только для test2.csv

  • Наконец, мы подводим итоги cat

Или, как предложил Оли, вы также можете использовать группирование команд:

$ { grep -vFf test2.csv test1.csv; grep -vFf test1.csv test2.csv; }
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Или просто запускайте один за другим, так как они оба пишут в STDOUT, в конечном итоге они будут добавлены:

$ grep -vFf test2.csv test1.csv; grep -vFf test1.csv test2.csv
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Если порядок строк не имеет значения, используйте awk или же perl:

awk '{seen[$0]++} END {for (i in seen) {if (seen[i] == 1) {print i}}}' 1.csv 2.csv

использование grep чтобы получить общие линии и отфильтровать их:

grep -hxvFf <(grep -Fxf 1.csv 2.csv) 1.csv 2.csv

Внутренний grep получает общие строки, затем внешний grep находит строки, которые не соответствуют этим общим линиям.

Использовать --*-line-format=... варианты diff

Ты можешь сказать diff именно то, что вам нужно - объяснено ниже:

diff --old-line-format='%L' --new-line-format='%L' --unchanged-line-format='' f1.txt f2.txt

Можно указать вывод diff очень детально, аналогично printf числовой формат.

Строки из первого файла, test1.csv называются "старыми" линиями, а строки из второго, test2.csv Есть "новые" строки. Это имеет смысл, когда diff используется, чтобы увидеть, что изменилось в файле.

Опции, которые нам нужны, это те, которые устанавливают формат для "старых" строк, "новых" строк и "неизмененных" строк.
Форматы, которые нам нужны, очень просты:
Для измененных строк, новых и старых, мы хотим вывести только текст строк. %L является символом формата для текста строки.
Для неизмененных строк мы ничего не хотим показывать.

При этом мы можем написать такие параметры, как --old-line-format='%L' и сложите все вместе, используя данные вашего примера:

$ diff --old-line-format='%L' --new-line-format='%L' --unchanged-line-format='' test1.csv test2.csv
100,4,2,1,7
100,300,500,700
21,22,23,24,25
50,25,700,5


Примечания по производительности

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

Существует возможность оптимизации использования diff с большими файлами: --speed-large-files, Он использует предположения о файловой структуре, поэтому неясно, поможет ли это в вашем случае, но стоит попробовать.

Параметры формата описаны в man diff под --LTYPE-line-format=LFMT,

Поскольку заказ не нужно сохранять, просто:

sort test1.csv test2.csv | uniq -u
  • sort test1.csv test2.csv: слияния и сортировки test1.csv а также test2.csv
  • uniq -u: печатает только те строки, которые не имеют дубликатов
Другие вопросы по тегам