Різниця двох великих файлів


14

У мене є "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

Я хочу вивести лише різницю, таким чином результат.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.

тож результат.csv повинен призвести до ~ 4998700 рядків

Я також спробував, grep -F -x -v -f що не вийшло.



1
@Tim я побачив ваше посилання, і я старий член, тому я знаю правила, але я був недбалий, вибачте :) редагував його, і я побачив спливаюче вікно, що повідомлення було відредаговано, щоб ви зробили роботу для мене, і я вдячний сер.
Ліноб

50,25,125,310є загальним для обох файлів ... вам потрібно видалити це з потрібного результату ..
heemayl

Чи слід зберігати порядок?
kos

1
Вибір залежить від того, що ви хочете зробити з інформацією, diff, IMO, - це зробити виправлення. У будь-якому випадку я зараз впевнений у вашому найкращому інструменті, diff, grep, awk або perl.
Пантера

Відповіді:


21

Здається, як робота для 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

Вас не обдурили пробіли після 200,[...]лінії, так? :)
kos

@kos ні, спочатку я видалив пробіли з файлів. Я припускав, що файли ОП насправді їх не мають.
тердон

6

Використання 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

1
Чому catдві перенаправлені команди? Чому б просто не запустити один за другим? grep ... ; grep ...або { grep ... ; grep ... ; }якщо ви хотіли щось зробити з колективним результатом.
Олі

@ Олі спасибі ... це чудова ідея .. я не думав про це ..
heemayl

4

Якщо порядок рядків не має значення, використовуйте 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

Внутрішній греп отримує загальні лінії, тоді зовнішній греп знаходить лінії, які не відповідають цим загальним лініям.


Ваша команда awk просто повторно реалізує sort | uniq -u, що дає неправильну відповідь, коли один файл містить повторювані рядки. Для грепу я б сказав "внутрішній" / "зовнішній", а не "внутрішній" / "зовнішній".
Пітер Кордес

@PeterCordes так, це так і хто ти, щоб сказати, що це неправильний результат?
муру

Неправильно в тому сенсі, що це не зовсім те, про що задавали питання, в тому кутовому випадку. Це може бути те , що хто - то хоче, але ви повинні вказати на різницю між тим, що ваш awkбуде друкувати і то , що comm -3і diffвідповіді будуть друкувати.
Пітер Кордес

@PeterCordes знову, хто ти це кажеш? Поки ОП не каже, що вони хочуть, мені байдуже, чи відрізняється вихід від результатів comm -3. Я не бачу жодної причини, чому я повинен пояснити це. Якщо ви хочете редагувати у примітці, не соромтеся.
муру

ОП заявила, що хоче різниці. Це не завжди те, що виробляє ваша програма. Програма, яка виробляє однаковий вихід для однієї проби, але не відповідає опису, як написано для всіх випадків, вимагає підняття головою. Я тут, щоб сказати це, і це правда незалежно від того, хто я чи хто ти. Я додав замітку.
Пітер Кордес

4

Використовуйте --*-line-format=...параметриdiff

Ви можете diffточно сказати , що вам потрібно - пояснено нижче:

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

Можна вказати вихідний розріз дуже детально, подібно до 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.


3

Оскільки замовлення не потрібно зберігати, просто:

sort test1.csv test2.csv | uniq -u
  • sort test1.csv test2.csv: злиття і сортування test1.csvіtest2.csv
  • uniq -u: друкує лише рядки, які не мають дубліката

Це не працює, якщо один файл містить рядок два рази, що не відображається в іншому файлі. Обидві випадки матимуть diffрезультат.
Volker Siegel
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.