У меня есть файл 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) времени.
Вместо этого попробуйте comm (при условии, что 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) (размер exclude-these.txt) + O (n) (размер from-this.txt)
exclude-these.txt
оно пустое. Ответ @ jona-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 - длины файлов. В моем случае n было ~ 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)
вот небольшой тест, любезно предоставленный user576875, но со 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 .
Очевидно, не будет работать с большими файлами, но это помогло мне. Несколько примечаний: