テキストファイル内の重複行を削除するにはどうすればよいですか?

私の巨大な(最大2 GiB)テキストファイルには、その中のすべての行の約100の正確な複製が含まれています(私の場合、ファイルがそうであるため、役に立たない) CSVのようなデータテーブル)。

必要なのは、元のシーケンスの順序を維持しながら(できれば、パフォーマンスを大幅に向上させるために犠牲にすることができます)、すべての繰り返しを削除することです。結果として、各行は一意になります。 100の等しい行があった場合(通常、重複はファイル全体に広がり、隣接するものではありません)、残っているのは1つだけです。

Scalaでプログラムを作成しました(考えてみてください)。これを実装するために、Scalaについて知らない場合はJava)。しかし、これをより速く実行できる、より高速なCで記述されたネイティブツールがあるのではないでしょうか?

更新:awk "!seen[$0]++" filenameソリューションは、ファイルがあれば問題なく機能しているように見えました。 2 GiB以下でしたが、8 GiBファイルをクリーンアップしようとすると、動作しなくなります。4GiBRAMを搭載したMacと4GiBRAMを搭載した64ビットWindows7PCでは無限大になるようです。 6 GiBスワップはメモリ不足になります。この経験から、4 GiBRAMを搭載したLinuxで試してみることに熱心ではありません。

コメント

  • これにより注文が破棄されますが、sort -uを試したことがありますか?このような大容量のファイルで実行できるかどうかはわかりません
  • CはJavaよりも大幅に高速ではないことがよくあります。 ‘現在(順番に)実行しているので、’かなりのチャンスがあります’ llが終了し、それを実装すると、実行が終了します。順不同の場合、sort -uの方がおそらく高速です。

回答

#bash(Freenode)で見られるawkソリューション:

awk "!seen[$0]++" filename 

コメント

  • 2Gファイルでこれを試したところ、ノートブックで3分かかりました。悪くない。私もuniqファイル名を試しました| awk ‘!seen [$ 0] ++ ‘ですが、’ではありませんでしたより高速です。
  • @HashWizard:このコマンドは並べ替えられませんが、同じ行が次に出現するたびに削除されます
  • このコマンドがどのように機能するのか疑問に思っていますか? -こちらをご覧ください: unix.stackexchange.com/questions/159695/how-does-awk-a0-work
  • @MaxWilliams yes 、ランダムに分散されていれば機能します。
  • 改行またはスペースのある行を保持しますawk '/^\s*?$/||!seen[$0]++'

回答

sort。ほとんどの実装では、巨大なファイルに対して特定の最適化が行われます(優れた外部ソートアルゴリズム)。この方法の利点は、特別な目的のユーティリティ内のすべての行のみをループし、インタプリタ言語内ではループしないことです。

<input nl -b a -s : | # number the lines sort -t : -k 2 -u | # sort and uniquify ignoring the line numbers sort -t : -k 1n | # sort according to the line numbers cut -d : -f 2- >output # remove the line numbers 

すべての行が空白以外の文字の場合、いくつかのオプションを省略できます。

<input nl | sort -k 2 -u | sort -k 1n | cut -f 2- >output 

大量の複製の場合、1つのコピーを保存するだけでよい方法メモリ内の各行のパフォーマンスが向上します。解釈のオーバーヘッドがありますが、「そのための非常に簡潔なawkスクリプトがあります(すでに enzotibによって投稿されています):

<input awk "!seen[$0]++" 

簡潔さ:!seen[$0] {print} {seen[$0] += 1}つまり、現在の行がまだ表示されていない場合は印刷してから、seenこの行のカウンター(初期化されていない変数または配列要素の数値は0)。

長い行の場合、各行のスプーフィング不可能なチェックサム(暗号化ダイジェストなど)のみを保持することでメモリを節約できます。 。たとえば、SHA-1を使用すると、必要なのは20バイトと1行あたりの一定のオーバーヘッドだけです。しかし、ダイジェストの計算はかなり遅いです。この方法は、高速のCPU(特に、ダイジェストを計算するためのハードウェアアクセラレータを備えたもの)があり、ファイルのサイズに比べてメモリが多くなく、行が十分に長い場合にのみ勝ちます。各行のチェックサムを計算できる基本的なユーティリティはありません。 Perl / Python / Ruby /…の解釈のオーバーヘッドを負担するか、専用のコンパイル済みプログラムを作成する必要があります。

<input perl -MDigest::MD5 -ne "$seen{Digest::MD5::md5($_)}++ or print" >output 

コメント

  • @Gilles awk '!seen[$0]++'の説明に基づくと、awkが2つの重複行を検出した場合、常に最初の行を保持し、すべてを無視することを意味しますか?後続のもの?(または最後のものを保持しますか?)
  • @ user779159最初のものを保持します:各入力行はすぐに出力されるか(最初の出現)、まったく印刷されない(繰り返し出現)。
  • しかし、それはsort -uとどのように比較されますか…?
  • @HashWizardプレーンなsort -uは順序を変更します。私の答えは、順序(正確には最初の出現の順序)を保持するソリューションを示しています。
  • @Gillesは、50%重複する大きなファイル(10G)のsort-uよりも高速だと思います。 ?

回答

sort -u big-csv-file.csv > duplicates-removed.csv 

出力ファイルは

コメント

  • 他の回答のawkコマンドほど速くはありませんが、概念的には簡単です!
  • @Johannこれは、数十万(さらには数百万)の短い改行で終了する文字列を含むファイルで頻繁に実行しています。私が行っている実験では、かなり迅速に結果が得られます。何度も実行されるスクリプトで使用する場合は、さらに重要になる可能性があります。時間の節約はかなりのものになる可能性があります。
  • sort -uを使用して、並べ替え中に重複を削除します。後ではなく。 (そしてメモリ帯域幅を節約します)それを別のプログラムにパイプします)。これは、出力を並べ替える場合にのみ、awkバージョンよりも優れています。 (この質問のOPは、元の注文を保存することを望んでいるため、これは少し異なるユースケースに適した回答です。)
  • 私にとっては、1分ほどかかります。 550万行のファイル(合計1.8 GB)。素晴らしい。

回答

重複排除されたファイルと同じ量をメモリに保持できると仮定します(データが実際に100倍、つまり約20MiB +オーバーヘッドで複製されている場合は、Perlを使用してこれを非常に簡単に行うことができます。

$ perl -ne "print unless $dup{$_}++;" input_file > output_file 

これ順序も保持されます。

必要に応じて、追加の無料ボーナスとして、%dupハッシュから各行の出現回数を抽出できます。

awkを好む場合は、これも行う必要があります(perlバージョンと同じロジック、同じ順序、dup変数):

$ awk "{if (++dup[$0] == 1) print $0;}" input_file > output_file 

コメント

  • これは良すぎる@ Mat、Iファイルを丸呑みしようとしていました、笑;-)
  • @ManAtWorkがsedとawkの魔法の力を待っています:-)
  • awkのヒントが再び素晴らしいです:- )
  • perlスクリプトを変更して削除のみにすることはできますか隣接する行を複製しますか?
  • @dumbledad:uniqそれをすべて単独で行います

回答

インプレースサポートを提供する他の回答はないため、次の1つを示します。

gawk -i inplace "!a[$0]++" file 

コメント

  • これは順序を保持しますか?ちなみに、これは私にはうまくいきませんでした。私のバージョンは次のとおりです:GNU Awk 4.0.2
  • @Leonidはい、そうです。一意の行の最初の出現を出力します。インプレースサポートは、2013年にリリースされたバージョン4.1で最初に導入されました。
  • これが答えになるはずです。 ‘は、既存または現在のファイル内の重複した文字列を実際に削除します。ここでの上位の回答とほとんどの回答は、一意の/重複した文字列のみを出力し、何もせずに作成する必要があります。結果を保存するための別の出力。

回答

uniq http://www.computerhope.com/unix/uuniq.htm

uniqファイル内の繰り返し行をレポートまたはフィルターで除外します。

コメント

  • 回答する場合は、なぜあなたの答えがなのかについての説明が1つです。では、この回答は以前の回答のいくつかとどのように異なりますか?
  • uniqのマニュアルページから:注:'uniq' does not detect repeated lines unless they are adjacent.したがって、最初に並べ替えて緩める必要があります。重複しない行の順序。

回答

Python Oneライナー:

python -c "import sys; lines = sys.stdin.readlines(); print "".join(sorted(set(lines)))" < InputFile 

コメント

  • これにより、ファイル全体がメモリに丸呑みされ、OP ‘の問題に適さない場合があります。また、順序を維持する保証はありません
  • 提案をありがとう、私は’ Pythonを学んでいます。学習目的でこれを試しました。:)
  • ここ’ s は、ワンライナーではありませんが(簡潔に)Python2.7バージョンです。ファイル全体をメモリにロードしたり、印刷するためにフィードする単一の巨大な文字列を作成したりせずに、順序を維持した一意の行を返します
  • @ 1_CRに感謝します今日何かを学びました:)OrderedDict

回答

ここでの回答はどれも私のMacではうまくいかなかったので、簡単なPythonを作成しました私のために働くスクリプト。先頭/末尾の空白を無視しており、メモリ消費も気にしません。

import sys inputfile = sys.argv[1] outputfile = sys.argv[2] with open(inputfile) as f: content = f.readlines() content = [x.strip() for x in content] my_list = list(set(content)) with open(outputfile, "w") as output: for item in my_list: output.write("%s\n" % item) 

上記を一意に保存します。pyして、次のように実行します:

python unique.py inputfile.txt outputfile.txt 

回答

元のシーケンス順序を維持しないソリューション

次のコードピースで実行しました。

sort duplicates.txt | uniq > noDuplicates.txt 

sortコマンドは行をアルファベット順に並べ替え、uniqコマンドは重複を削除します。

注: 最初に行を並べ替えたのは、uniqは、隣接していない限り重複行を検出しません。

コメント

  • 質問はメソッドを要求します(できれば)入力順序を維持します。それに対処するためにあなたの答えを編集できますか?入力順序を維持するsortを使用した既存の回答と、iv id = “70fbddf299を使用した 1つの回答があることに注意してください。 “>

入力順序を維持せずに、uniqにパイプするよりも効率的な方法で。

  • @StephenKitt編集。他の回答を調べましたが、’基本的なコマンドだけでは何も見つかりませんでした。フィードバックをお寄せいただきありがとうございます。
  • 基本的なコマンドのみ、実際には1つのコマンドiv idのみを含む回答へのリンクを提供しました= “f0e9e85169”> ( POSIX の一部です);-)
  • @StephenKittその答えを見ました。鉱山も問題を処理する方法です。もっと何をしてほしいですか?回答を削除する必要がありますか?
  • いいえ、回答を削除しないでください。 「’基本的なコマンドだけでは何も見つからなかった」と言ったので、他の答えを知っていることを確認したかっただけです。
  • 回答

    bash 4を使用すると、連想配列を利用する純粋なbashソリューションになります。 を使用できます。次に例を示します

    unset llist; declare -A llist; while read -r line; do if [[ ${llist[$line]} ]]; then continue else printf "%s\n" "$line" llist[$line]="x" fi done < file.txt 

    コメント

    • ドン’ t readループを使用して大きなテキストファイルを処理します。 bashは、改行のオーバーシュートを回避するために、一度に1バイトを読み取る必要があります。また、Bashは、awkと比較して、一般的にテキスト処理がそれほど高速ではありません。これを使用する場合、read -raは入力でバックスラッシュを食べないようにします。また、これをシェル関数に配置する場合は、’ループのunset llist を忘れないでください。インタラクティブに使用してください。
    • @PeterCordes、またはこれ 🙂

    コメントを残す

    メールアドレスが公開されることはありません。 * が付いている欄は必須項目です