行が長い非常に大きなファイルから文字列を取得する方法は?

行が長い非常に大きなファイルから文字列を取得する方法は?

grep昨日、誤って誤ったものを使用したことがわかりました。ちょうどbashの記録を確認し、私が何をしているかを確認しました。

grep search-string-here -f large-file-with-long-lines.txt

これがメモリ枯渇の原因です。

実装する:

grep search-string-here large-file-with-long-lines.txt

...欲しい動作があります。

同様のエラーのある問題を指摘してくれた@αГsнιιに感謝し、行の長さとメモリのgrep使い方に関する仮定を修正してくれた@EdMortonと@ilkkachuに感謝します。awk

以下は、元の質問(8GB RAMに対応できない長いワイヤについて間違っていますが)と@EdMortonが受け入れた答えです。

文字列を検索したい非常に長い行(8 GB RAMには適していません)を持つ非常に大きなファイル(100 GB以上)があります。行全体をメモリに合わせようとしているgrepので、これを行うことはできません。grep

これまで私が思いついた最高のソリューションは次のとおりです。

awk '/search-string-here/{print "Found."}' large-file-with-long-lines.txt

私は実際にこの解決策に満足していますが、これを行うより直感的な方法があるかどうか疑問に思います。たぶん別の実装がありますかgrep

答え1

これは、検索したい文字列(または正規表現)に表示されない文字がある場合にのみ機能する単純な部分的な解決策ですが、オンラインで頻繁に登場その文字発生の間のスペースが常にメモリに収まるようにします。たとえば、各行が比較的短いセミコロンで区切られたフィールドで構成される非常に長いリストであるとします。

<large-file-with-long-lines.txt tr ';' '\n' | grep 'search-string-here'

これは別の部分的な解決策です。発生回数は常に行の先頭からN文字の倍数で始まります。、いくつかの固定Nの場合、それを使用しますfold改行とag複数行検索を実行します。この例では、その項目は常に行の先頭の後ろから3 * x文字で始まることが知られています。

<large-file-with-long-lines.txt fold -w3 | ag $'cat\ntag\ngag\nact'

これは、各オフセットを繰り返し検索して任意の文字列検索に一般化できます。

<large-file-with-long-lines.txt fold -w3 | ag $'fee\n-fi\n-fo\n-fu\nm|fe\ne-f\ni-f\no-f\num|f\nee-\nfi-\nfo-\nfum'

文字列がほとんど存在しますが、途中で改行があると、偽の肯定が発生する可能性があります。

答え2

入力ファイルを何度も読み取るために遅くなりますが(検索する文字列の各文字ごとに1回、検索文字列のサイズを文字列から読み取るたびに)機能する必要があります。複数の文字RSの合計にGNU awkを使用してすべてのサイズRT(テストされていません):

awk -v str='search-string-here' '
    BEGIN {
        lgth = length(str)
        RS = sprintf("%*s",lgth,"")
        gsub(/ /,".",RS)
        for (i=1; i<lgth; i++) {
            ARGV[ARGC++] = ARGV[1]
        }
    }
    RT == str {
        print "found"
        exit
    }
' file

RS を N s に設定します.(ここで N は検索文字列の長さ)。入力から文字#1で始まるすべてのN文字チェーンを読み取り、それを検索文字列と比較します。入力の現在のN文字が文字の場合、検索と一致すると文字列は終了します。そのパスに一致するものがない場合は、同じ操作をやり直しますが、char#2から始めてN回完了するまで続くので、比較する入力ファイルに長さNの文字列がありません。検索文字列として。

上記は正規表現比較ではなく文字列比較を実行していることに注意してください。正規表現比較を実行するには、一致する文字列の最大長を別の方法で決定し、正規表現比較演算子を代わりに~使用する必要があります==。たとえば、入力で一致する文字列が20文字を超えることができないことがわかっている場合は、これを行うことができます。

awk -v regexp='search-regexp-here' '
    BEGIN {
        lgth = 20
        RS = sprintf("%*s",lgth,"")
        gsub(/ /,".",RS)
        for (i=1; i<lgth; i++) {
            ARGV[ARGC++] = ARGV[1]
        }
    }
    RT ~ regexp {
        print "found"
        exit
    }
' file

ただし、正規表現検索には、文字列検索で考慮する必要がないいくつかの欠陥があります。たとえば、検索正規表現が境界としてまたはを^含む$場合、読み取り時に各文字列の開始/終了境界の周りに文字が生成されるため、上記の誤った一致が得られます。 N文字の長さの文字列。

関連情報