テキストファイル内の重複行をカウントする
1. 概要
Linuxコマンドラインを使用する場合、テキストファイルの処理は一般的な操作です。 場合によっては、重複した行を含むテキストファイルが発生することがあります。 このチュートリアルでは、テキストファイルの繰り返し行をカウントする方法を学習します。
2. 問題の紹介
重複行をカウントする方法を簡単に説明するために、サンプルテキストファイルinput.txtを作成しましょう。
$ cat input.txt
I will choose MAC OS.
I will choose Linux.
I will choose MAC OS.
I will choose Linux.
I will choose MAC OS.
I will choose Linux.
I will choose Linux.
I will choose Microsoft Windows.
I will choose Linux.
I will choose Linux.
上記の出力が示すように、input.txtには重複した行が含まれています。 次に、各行の出現回数をカウントします。
このチュートリアルでは、問題を解決するための2つのアプローチについて説明します。
その後、2つのアプローチを比較し、どちらが問題のより良い解決策になるかについて説明します。
3. sortコマンドとuniqコマンドの組み合わせ
uniqコマンドには、入力ファイル内の出現回数をカウントするための便利な-cオプションがあります。 これはまさに私たちが探しているものです。
ただし、注意しなければならないことの1つは、 -cオプションを指定したuniqコマンドは、重複した行が隣接している場合にのみ機能することです。 つまり、最初に繰り返し行をグループ化する必要があります。 sort コマンドは、それを手に入れることができます。
最初にinput.txtをソートし、結果を-cオプションを指定してuniqにパイプします。
$ sort input.txt | uniq -c
6 I will choose Linux.
3 I will choose MAC OS.
1 I will choose Microsoft Windows.
出力が示すように、各行の出現回数が行と一緒に出力されます。 問題は解決された。
4. awkコマンドの使用
または、非常に単純なawkワンライナーを使用してこの問題を解決することもできます。
$ awk '{ a[$0]++ } END{ for(x in a) print a[x], x }' input.txt
1 I will choose Microsoft Windows.
6 I will choose Linux.
3 I will choose MAC OS.
上記の出力から、awkワンライナーでも問題が解決したことがわかります。
ここで、awkコードがどのように機能するかを理解しましょう。
- {a [$ 0] ++} :連想配列( a [KEY] )を作成して、行と出現回数を記録しました。 KEY は入力ファイルの行であり、値 a[KEY]はKEYの出現回数です。
- END {for(x in a)print a [x]、x} :すべての行を処理した後、ENDブロックを使用して配列内のすべての要素を出力しました
5. 2つのソリューションの比較
sortおよびuniqコマンドを使用したソリューションは便利です。 同様に、awkソリューションも非常に簡単です。 質問したい場合がありますが、どちらがより良い解決策ですか?
このセクションでは、パフォーマンス、柔軟性、および拡張性の観点から2つのソリューションを比較してみましょう。
5.1. より大きな入力ファイルの作成
input.txt には10行しかないため、どちらのアプローチでも問題の解決は非常に高速です。
2つのソリューションのパフォーマンスをよりよく比較するために、単純なシェルスクリプトcreate_input.shを使用してより大きな入力ファイルを生成します。
#!/bin/sh
# the output file
BIG_FILE="big_input.txt"
# total number of lines
TOTAL=1000000
# an array to store lines
ARRAY=(
"I will choose Linux."
"I will choose Microsoft Windows."
"I will choose MAC OS."
)
# remove the file
rm -f "$BIG_FILE"
while (( TOTAL > 0 )) ; do
echo ${ARRAY[$(( $RANDOM % 3 ))]} >> $BIG_FILE
(( TOTAL-- ))
done
上記のスクリプトでは、ARRAYという名前のBash配列に3行を保存しています。 次に、 while ループで、配列からランダムに1行を選択し、big_input.txtというファイルに書き込みます。
スクリプトを実行すると、100万行のファイルが得られます。
$ wc -l big_input.txt
1000000 big_input.txt
次に、このファイルを入力として使用して、2つのソリューションのパフォーマンスを比較します。
5.2. パフォーマンス
time コマンドを使用して実行時間を測定し、各ソリューションをこの大きな入力ファイルに適用してみましょう。
まず、ソートをテストしましょう| uniq コマンド:
$ time (sort big_input.txt | uniq -c)
333814 I will choose Linux.
333577 I will choose MAC OS.
332609 I will choose Microsoft Windows.
real 0m0.766s
user 0m1.995s
sys 0m0.053s
次に、awkコマンドをテストします。
$ time awk '{a[$0]++}END{for(x in a)print a[x], x}' big_input.txt
333814 I will choose Linux.
333577 I will choose MAC OS.
332609 I will choose Microsoft Windows.
real 0m0.190s
user 0m0.182s
sys 0m0.001s
上記のテスト結果は、awkコマンドがsort|よりもはるかに高速(このマシンでは約4倍高速)であることを明確に示しています。 uniqの組み合わせ。 それの訳は:
- awk コマンドは単一のプロセスのみを開始しますが、 sort | uniqアプローチには2つのプロセスが必要です
- awk コマンドはファイルを1回だけ処理しますが、 sort | uniq の組み合わせは、入力ファイルのすべての行を2回処理する必要があります
- sort コマンドは、ファイルをさらにソートします。 したがって、複雑さは awkコマンドよりも高くなります。O(nLog(n))> O(n)
5.3. 柔軟性と拡張性
uniq-cコマンドは便利です。 ただし、出力の形式は固定されています。 出力を調整したい場合は、他のテキスト処理ユーティリティを使用する必要があります。 さらに、これによりプロセスが追加され、出力の処理回数が増えます。
反対側では、
たとえば、各行の後にカウントを入れましょう。
$ awk '{ a[$0]++ } END{ for(x in a) printf "%s [ count: %d ]\n", x, a[x] }' input.txt
I will choose Microsoft Windows. [ count: 1 ]
I will choose Linux. [ count: 6 ]
I will choose MAC OS. [ count: 3 ]
さらに、強力な awk 言語のおかげで、 awkコマンドを簡単に拡張して、より複雑な要件を処理できます。
たとえば、3回以上重複している行のみを出力する場合は次のようになります。
$ awk '{ a[$0]++ } END{ for(x in a) if(a[x]>3) print a[x], x }' input.txt
6 I will choose Linux.
または、より詳細なレポートを取得したい場合:
$ awk '{ a[$0]++ } END{ for(x in a) printf "%.2f%% (%d in %d): %s\n",100*a[x]/NR,a[x],NR, x }' input.txt
10.00% (1 in 10): I will choose Microsoft Windows.
60.00% (6 in 10): I will choose Linux.
30.00% (3 in 10): I will choose MAC OS.
6. 結論
この記事では、テキストファイル内の重複行をカウントする2つの異なる方法を学びました。 その後、パフォーマンス、柔軟性、および拡張性の観点から2つのソリューションを比較しました。
ソート| uniqの組み合わせは簡単です。 ただし、特に大きなファイルを処理する必要がある場合は、awkソリューションの方が適しています。