Bir dosyam var f1
:
line1
line2
line3
line4
..
..
Başka bir dosyadaki tüm satırları silmek istiyorum f2
:
line2
line8
..
..
Ben bir şey denedim cat
ve sed
hatta yakın hangi amaçla hareket değildi ki. Bunu nasıl yapabilirim?
Bir dosyam var f1
:
line1
line2
line3
line4
..
..
Başka bir dosyadaki tüm satırları silmek istiyorum f2
:
line2
line8
..
..
Ben bir şey denedim cat
ve sed
hatta yakın hangi amaçla hareket değildi ki. Bunu nasıl yapabilirim?
Yanıtlar:
grep -v -x -f f2 f1
hile yapmalı.
Açıklama:
-v
eşleşmeyen satırları seçmek için-x
sadece tüm satırları eşleştirmek için-f f2
kalıpları almak f2
Bir yerine kullanabilir grep -F
veya fgrep
maç için sabit dizeleri gelen f2
ziyade desenleri (eğer yerine çizgi tedavi daha bir "eğer sen görmek ne elde" şekilde satırları kaldırmak istediğiniz f2
regex desenleri gibi).
grep
. f2
Aramaya başlamadan önce düzgün bir şekilde ön işlem yaparsa , arama sadece O (n) zaman alacaktır.
Onun yerine haberleşmeyi deneyin (f1 ve f2'nin "zaten sıralanmış" olduğunu varsayarak)
comm -2 -3 f1 f2
comm
çözümün, f1
kullanım için ön koşul olan satırların sıralandığını göstermediğinden emin değilimcomm
comm -2 -3 <(sort f1) <(sort f2)
Çok büyük olmayan dosyaları dışlamak için, AWK'nın ilişkilendirilebilir dizilerini kullanabilirsiniz.
awk 'NR == FNR { list[tolower($0)]=1; next } { if (! list[tolower($0)]) print }' exclude-these.txt from-this.txt
Çıktı, "from-this.txt" dosyasıyla aynı sırada olacaktır. tolower()
Bunu gerekirse fonksiyon, küçük harf duyarsız hale getirir.
Algoritmik karmaşıklık muhtemelen O (n) (bu.txt boyutunu dışla) + O (n) (this.txt boyutundan) olacaktır.
exclude-these.txt
, boşsa başarısız olur (yani herhangi bir çıktı üretmez) . @ jona-christopher-sahnwaldt'ın aşağıdaki cevabı bu durumda işe yarar. Birden fazla dosya da belirtebilirsiniz, örn.awk '{if (f==1) { r[$0] } else if (! ($0 in r)) { print $0 } } ' f=1 done.out failed.out f=2 all-files.out
Dennis Williamson'ın cevabına benzer şekilde (çoğunlukla sözdizimsel değişiklikler, örneğin numara yerine dosya numarasını açıkça belirlemek 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
Erişim r[$0]
, o satır için bir giriş oluşturur, bir değer ayarlamaya gerek yoktur.
Awk'nin sabit arama ve (ortalama olarak) sabit güncelleme süresine sahip bir hash tablosu kullandığını varsayarsak, bunun zaman karmaşıklığı O (n + m) olacaktır, burada n ve m dosyaların uzunluklarıdır. Benim durumumda, n ~ 25 milyon ve m ~ 14000 idi. Awk çözümü sıralamadan çok daha hızlıydı ve ben de orijinal sırayı korumayı tercih ettim.
f
daha net buluyorum NR == FNR
, ama bu bir zevk meselesi. Hash içine atama o kadar hızlı olmalı ki, iki sürüm arasında ölçülebilir bir hız farkı olmamalıdır. Sanırım karmaşıklık konusunda yanılmışım - eğer arama sabitse, güncelleme de sabit olmalıdır (ortalama olarak). Güncellemenin neden logaritmik olacağını düşündüğümü bilmiyorum. Cevabımı düzenleyeceğim.
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
. Oysa diğer awk
çözüm boş dışlama dosyasıyla başarısız olur ve yalnızca birini alabilir.
Ruby'niz varsa (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) karmaşıklığına sahip olan. Performansı önemsemek istiyorsanız, işte başka bir versiyon
b=File.read("file2").split
a=File.read("file1").split
(a-b).each {|x| puts x}
Bu, çıkarma işlemini gerçekleştirmek için bir hash kullanır, dolayısıyla karmaşıklık O (n) (a'nın boyutu) + O (n) (b'nin boyutu)
aşağıda user576875'in izniyle, ancak 100 bin satırla yukarıdakilerin küçük bir ölçütü:
$ 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
oluşturulan 2 dosya arasında hiçbir fark olmadığını göstermek için kullanıldı.
Diğer çeşitli cevaplar arasında bazı zamanlama karşılaştırmaları:
$ 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
her iki dosyada birden çok kez görünen satırları kaldırdığı için simetrik bir fark bile değildir.
comm ayrıca stdin ve burada dizelerle de kullanılabilir:
echo $'a\nb' | comm -23 <(sort) <(sort <<< $'c\nb') # a
Görünüşe göre SQLite kabuğu için uygun bir iş:
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
'Programlama' cevabı değil ama işte hızlı ve kirli bir çözüm: http://www.listdiff.com/compare-2-lists-difference-tool adresine gitmeniz yeterli .
Açıkçası büyük dosyalar için işe yaramayacak ama benim için hile yaptı. Birkaç not: