ソートされたファイルを効率的に検索


12

各行に1つの文字列を含む大きなファイルがあります。文字列がファイルにあるかどうかをすばやく判断できるようにしたいと思います。理想的には、これはバイナリチョップタイプのアルゴリズムを使用して行われます。

一部のGooglingは、バイナリ検索アルゴリズムを使用して、特定のプレフィックスで始まるすべての文字列を検索して出力することを約束するフラグをlook付けてコマンドを明らかにしました-b。残念ながら、これは正しく機能していないようで、ファイル内にあることがわかっている文字列に対してnullの結果を返します(同等のgrep検索で正しく返されます)。

このファイルを効率的に検索するための別のユーティリティまたは戦略を知っている人はいますか?


トップの答えは間違ったソートを示しています:実際には、LC_COLLATE = C sort -dでlookコマンドを正しく機能させる必要があります。これは、lookがロケールを無視しているようで、ハードコードされたソートのようにCを使用するだけなので、バグも開きましたこの混乱する動作のため:bugzilla.kernel.org/show_bug.cgi
id

look -bエラーで失敗しましたFile too large。全部をメモリに読み込もうとしていると思います。
ブライアンミントン2018

回答:


9

との間には本質的な違いがgrepありlookます。

特に明記しない限り、行内のgrepどこかにでもパターンが見つかります。以下のためのlookマニュアルページの状態:

look — 指定された文字列で始まる行を表示する

私はあまり使用していませんlookが、試したばかりの簡単な例では問題なく動作しました。


1
検索する必要があるファイルの行数は約110,000,000行です。実行するとegrep "^TEST" sortedlist.txt | wc -l 41,289件の結果が得られます。ただし、同等のlookコマンドでlook -b TEST sortedlist.txt | wc -lは、1995年の結果しか得られません。にバグがあるのでしょうかlook
マット

1
@Matt lookは、ファイルのソートに使用したプログラムとは異なる照合設定を使用している可能性があります。
kasperd 2015年

4

多分少し遅い答え:

Sgrepが役立ちます。

Sgrep(ソートされたgrep)は、ソートされた入力ファイルで検索キーに一致する行を検索し、一致した行を出力します。大きなファイルを検索する場合、sgrepは従来のUnix grepよりもはるかに高速ですが、大きな制限があります。

  • すべての入力ファイルは、通常のファイルでソートする必要があります。
  • ソートキーは行の先頭から開始する必要があります。
  • 検索キーは行の先頭でのみ一致します。
  • 正規表現のサポートはありません。

ここからソースをダウンロードできます:https : //sourceforge.net/projects/sgrep/? source =typ_redirect

ここのドキュメント:http : //sgrep.sourceforge.net/

別の方法:

ファイルのサイズがわからないので、並列処理を試してみてください:

/programming/9066609/fastest-possible-grep

私は常にサイズが100GBを超えるファイルでgrepを実行しています。


2
それはすでにaskubuntu.com/a/701237/158442にありますか?
muru

はい、ダウンロードリンクに記入します...
メモリボックス2018

それがすべてである場合は、新しい回答を投稿する代わりに、その投稿を編集する必要があります
muru

その投稿をお勧めします:sudo apt-get install sgrep sgrepを取得するには、buntuリポジトリのsgrepは実際にはこのsgrepではありません。同じものかどうかはわかりません。
メモリボックス2018

0

ファイルを断片にハッシュしてから、必要な断片だけをgrepすることができます。

for line in $(cat /usr/share/dict/american-english | tr '[:upper:]' '[:lower:]' | sort | uniq)
do
    prefix=$(echo $line | md5sum - | cut -c 1-2)
    mkdir -p $prefix
    echo $line | gzip >> $prefix/subwords
done

ルックアップは次のようになります。

    prefix=$(echo $word | md5sum - | cut -c 1-2)
    zgrep -m 1 -w word $prefix/subwords

これは2つのことを行います。

  1. 圧縮ファイルの読み書き。一般に、ディスク(非常に遅い)ではなく、CPUに負荷をかける(非常に速い)方が高速です。
  2. ほぼ等しい分布を得るためにハッシュする、各ピースのサイズを減らすために望むように短いまたは長いハッシュを使用できます(ただし、そうする場合はネストされたサブディレクトリを使用することをお勧めします)

0

sgrepはあなたのために働くかもしれません:

sudo apt-get install sgrep
sgrep -l '"needle"' haystack.txt

プロジェクトページhttp://sgrep.sourceforge.net/は言う:

sgrepはバイナリ検索アルゴリズムを使用します。これは非常に高速ですが、ソートされた入力が必要です。

ただし、挿入については、データベースを使用するよりも優れた解決策はないと思います:https : //stackoverflow.com/questions/10658380/shell-one-liner-to-add-a-line-to-a-sorted-file/ 33859372#33859372


3
sgrepUbuntuのリポジトリには、実際には、このsgrep「構造化パターンのファイルを検索」するために設計されており、バイナリ検索とは何の関係もありません。
ingomueller.net 2017

0

あなたがそれをしたい場合は、本当に速い(O(1)速い)あなたはに見てハッシュセットを構築することができます。ビルド済みのハッシュセットをファイルに保存し、ファイル全体をメモリに読み込まずにそれプローブできる実装を見つけることができなかったので、自分でロールバックしました

ハッシュセット(-b/ --build)を作成します。

./hashset.py --build string-list.txt strings.pyhashset

ハッシュセット(-p/ --probe)をプローブします。

./hashset.py --probe strings.pyhashset \
    'Is this string in my string list?' 'What about this one?'

…または標準入力で検索する文字列を使用:

printf '%s\n' 'Is this string in my string list?' 'What about this one?' |
./hashset.py --probe strings.pyhashset

終了ステータスのみに関心がある場合--probeは、-q/ --quietオプションを使用しての出力を停止できます。

if ./hashset.py --quiet --probe strings.pyhashset ...; then
    echo 'Found'
else
    echo 'Not found'
fi

その他のオプションについては、-h/ --helpオプションまたは付随するREADMEファイルからアクセスできる使用法の説明を参照してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.