Удаление строк из одного файла в другой файл



у меня есть файл f1:



line1
line2
line3
line4
..
..


Я хочу удалить все строки, которые находятся в другом файле f2:



line2
line8
..
..


Я пробовал что-то с cat и sed, который даже не был близок к тому, что я намеревался. Как я могу это сделать?

597   8  

8 ответов:

grep -v -x -f f2 f1 следует сделать трюк.

объяснение:

  • -v для выбора несоответствующих строк
  • -x чтобы соответствовать только целые строки
  • -f f2 чтобы получить шаблоны от f2

вместо этого можно использовать -F f2 в матче фиксированных строк С f2, а не шаблоны (в случае, если вы хотите удалить строки в "то, что вы видите, если то, что вы получаете", а не лечение строки в f2 в качестве шаблонов regex).

вместо этого попробуйте comm (предполагая, что f1 и f2 "уже отсортированы")

comm -2 -3 f1 f2

для исключения файлов, которые не слишком велики, вы можете использовать ассоциативные массивы AWK.

awk 'NR == FNR { list[tolower()]=1; next } { if (! list[tolower()]) print }' exclude-these.txt from-this.txt 

вывод будет в том же порядке, что и "от-этого".txt-файл. Элемент tolower() функция делает его нечувствительным к регистру, если вам это нужно.

алгоритмическая сложность, вероятно, будет O(n) (exclude-these.размер txt) + O (n) (из-за этого.тхт размер)

похоже на ответ Денниса Уильямсона (в основном синтаксические изменения, например, установка номера файла явно вместо NR == FNR трюк):

awk '{if (f==1) { r[] } else if (! ( in r)) { print } } ' f=1 exclude-these.txt f=2 from-this.txt

ссылке r[] создает запись для этой строки, не нужно устанавливать значение.

предполагая, что awk использует хэш-таблицу с постоянным поиском и(в среднем) постоянным временем обновления, временная сложность этого будет O (n + m), где n и m-длины файлов. В моем случае n было ~25 миллионов и м ~14000. Решение 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 был использован, чтобы показать, что нет никаких различий между 2 файлами, созданных.

некоторые сравнения времени между различными другими ответами:

$ 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

ты этой С помощью sed?

sed 's#^#sed -i '"'"'s%#g' f2 > f2.sh

sed -i 's#$#%%g'"'"' f1#g' f2.sh

sed -i '1i#!/bin/bash' f2.sh

sh f2.sh

Comments

    Ничего не найдено.