ファイルがあります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 size)+ O(n)(from-this.txt size)になります。
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は〜2500万、mは〜14000でした。awkソリューションはソートよりもはるかに高速で、元の順序を維持することも好みました。
f
よりも明確ですがNR == FNR
、それは好みの問題です。ハッシュへの割り当ては非常に高速であるため、2つのバージョン間で測定可能な速度の違いはありません。私は複雑さについて間違っていたと思います-ルックアップが一定の場合、更新も一定のはずです(平均して)。更新が対数になると思った理由がわかりません。回答を編集します。
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
ソリューションは空の除外ファイルで失敗し、1つしか取得できません。
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およびhere文字列でも使用できます。
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にアクセスしてください。
明らかに巨大なファイルでは機能しませんが、私にとってはうまくいきました。いくつかのメモ: