У мене є файл f1
:
line1
line2
line3
line4
..
..
Я хочу видалити всі рядки, що знаходяться в іншому файлі f2
:
line2
line8
..
..
Я спробував щось із, cat
і sed
це було навіть близько до того, що я мав намір. Як я можу це зробити?
У мене є файл f1
:
line1
line2
line3
line4
..
..
Я хочу видалити всі рядки, що знаходяться в іншому файлі f2
:
line2
line8
..
..
Я спробував щось із, cat
і sed
це було навіть близько до того, що я мав намір. Як я можу це зробити?
Відповіді:
grep -v -x -f f2 f1
повинен зробити трюк.
Пояснення:
-v
щоб вибрати невідповідні лінії-x
щоб відповідати лише цілим рядкам-f f2
щоб отримати зразки f2
Замість цього можна використовувати grep -F
або fgrep
для відповідності фіксованих рядків з , f2
а не моделей (в разі , якщо ви хочете видалити рядки в «то , що ви бачите , якщо то , що ви отримуєте» спосіб , а не обробляти рядки в f2
якості регулярних виразів шаблонів).
grep
. Якщо вона буде попередньо оброблена f2
належним чином перед початком пошуку, пошук займе лише O (n) час.
Спробуйте кому замість цього (припустимо, що f1 і f2 "вже відсортовані")
comm -2 -3 f1 f2
comm
, що рішення має питання, чи не вказує на те, що рядки в f1
сортованих, що є необхідною умовою для використанняcomm
comm -2 -3 <(sort f1) <(sort f2)
Для виключення не надто великих файлів, ви можете використовувати асоціативні масиви AWK.
awk 'NR == FNR { list[tolower($0)]=1; next } { if (! list[tolower($0)]) print }' exclude-these.txt from-this.txt
Вихід буде в тому ж порядку, що і файл "from-this.txt". Ця tolower()
функція робить її нечутливою до регістру, якщо вам це потрібно.
Алгоритмічна складність, ймовірно, буде O (n) (виключати-розмір цих.txt) + O (n) (від-this.txt розмір)
exclude-these.txt
він порожній. У цьому випадку відповідь @nana-christopher-sahnwaldt нижче. Ви також можете вказати кілька файлів, наприкладawk '{if (f==1) { r[$0] } else if (! ($0 in r)) { print $0 } } ' f=1 done.out failed.out f=2 all-files.out
Подібно до відповіді Денніса Вільямсона (переважно синтаксичні зміни, наприклад, встановлення номера файлу явно замість NR == FNR
хитрості):
awk '{if (f==1) { r[$0] } else if (! ($0 in r)) { print $0 } } ' f=1 exclude-these.txt f=2 from-this.txt
Доступ r[$0]
створює запис для цього рядка, не потрібно встановлювати значення.
Якщо припустити, що awk використовує хеш-таблицю з постійним пошуком і (в середньому) постійним часом оновлення, часова складність цього буде O (n + m), де n і m - довжини файлів. У моєму випадку п було ~ 25 мільйонів, а m ~ 14000. Рішення awk було набагато швидшим, ніж сортування, і я також вважав за краще дотримуватися початкового замовлення.
f
чіткішою NR == FNR
, але це питання смаку. Призначення в хеш має бути настільки швидким, щоб між двома версіями не було вимірної різниці швидкостей. Я думаю, що я помилявся щодо складності - якщо пошук постійний, оновлення має бути постійним (в середньому). Я не знаю, чому я вважав, що оновлення буде логарифмічним. Я відредагую свою відповідь.
awk '{if (f==1) { r[$0] } else if (! ($0 in r)) { print $0 } } ' f=1 empty.file done.out failed.out f=2 all-files.out
. Тоді як інше awk
рішення не працює з порожнім виключенням файлу і може приймати лише один.
якщо у вас є Ruby (1.9+)
#!/usr/bin/env ruby
b=File.read("file2").split
open("file1").each do |x|
x.chomp!
puts x if !b.include?(x)
end
Який має складність O (N ^ 2). Якщо ви хочете дбати про продуктивність, ось ще одна версія
b=File.read("file2").split
a=File.read("file1").split
(a-b).each {|x| puts x}
який використовує хеш для здійснення віднімання, так це складність O (n) (розмір a) + O (n) (розмір b)
ось невеликий орієнтир, люб’язно надаючи користувач576875, але із 100K рядками, із зазначеного вище:
$ for i in $(seq 1 100000); do echo "$i"; done|sort --random-sort > file1
$ for i in $(seq 1 2 100000); do echo "$i"; done|sort --random-sort > file2
$ time ruby test.rb > ruby.test
real 0m0.639s
user 0m0.554s
sys 0m0.021s
$time sort file1 file2|uniq -u > sort.test
real 0m2.311s
user 0m1.959s
sys 0m0.040s
$ diff <(sort -n ruby.test) <(sort -n sort.test)
$
diff
було використано, щоб показати, що немає відмінностей між двома створеними файлами.
Деякі порівняння часу між різними іншими відповідями:
$ for n in {1..10000}; do echo $RANDOM; done > f1
$ for n in {1..10000}; do echo $RANDOM; done > f2
$ time comm -23 <(sort f1) <(sort f2) > /dev/null
real 0m0.019s
user 0m0.023s
sys 0m0.012s
$ time ruby -e 'puts File.readlines("f1") - File.readlines("f2")' > /dev/null
real 0m0.026s
user 0m0.018s
sys 0m0.007s
$ time grep -xvf f2 f1 > /dev/null
real 0m43.197s
user 0m43.155s
sys 0m0.040s
sort f1 f2 | uniq -u
Це навіть не симетрична різниця, оскільки вона видаляє рядки, що з’являються кілька разів у будь-якому файлі.
comm також можна використовувати з stdin та тут рядками:
echo $'a\nb' | comm -23 <(sort) <(sort <<< $'c\nb') # a
Здається, робота, яка підходить для оболонки SQLite:
create table file1(line text);
create index if1 on file1(line ASC);
create table file2(line text);
create index if2 on file2(line ASC);
-- comment: if you have | in your files then specify “ .separator ××any_improbable_string×× ”
.import 'file1.txt' file1
.import 'file2.txt' file2
.output result.txt
select * from file2 where line not in (select line from file1);
.q
Відповідь не "програмування", але ось швидке та брудне рішення: просто перейдіть на сторінку http://www.listdiff.com/compare-2-lists-difference-tool .
Очевидно, що не працюватимуть для величезних файлів, але це зробило трюк для мене. Кілька приміток: