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 catve sedhatta 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 catve sedhatta 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 f2Bir yerine kullanabilir grep -Fveya fgrepmaç için sabit dizeleri gelen f2ziyade desenleri (eğer yerine çizgi tedavi daha bir "eğer sen görmek ne elde" şekilde satırları kaldırmak istediğiniz f2regex desenleri gibi).
grep. f2Aramaya 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, f1kullanı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.
fdaha 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: