如何在非常長的行的非常大的文件中搜尋字元串?
原來我
grep
昨天不小心用錯了。我剛剛檢查了我的 bash 歷史記錄並看到了我正在執行的操作:grep search-string-here -f large-file-with-long-lines.txt
這就是導致記憶體耗盡的原因。
執行:
grep search-string-here large-file-with-long-lines.txt
…具有所需的行為。
感謝@αғsнιη 指出有類似錯誤的問題,感謝@EdMorton 和@ilkkachu 糾正我對行長以及如何
grep
使用awk
記憶體的假設。以下是最初的問題(儘管我認為長線無法放入 8 GB 的 RAM 似乎是錯誤的)和 @EdMorton 接受的答案。
我有一個非常大的文件(超過 100 GB),其中的行很長(甚至無法放入 8 GB RAM),我想在其中搜尋一個字元串。我知道
grep
不能這樣做,因為grep
試圖將整行放入記憶體。到目前為止,我想出的最佳解決方案是:
awk '/search-string-here/{print "Found."}' large-file-with-long-lines.txt
我實際上對這個解決方案很滿意,但我只是想知道是否有一些更直覺的方法可以做到這一點。也許其他一些實現
grep
?
這會很慢,因為它會多次讀取輸入文件(您要查找的字元串中的每個字元一次,每次在字元串中該搜尋字元串的大小)但它應該適用於任何大小的文件,任何大小的行,將 GNU awk 用於多字元
RS
和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 是搜尋字元串的長度,從 char #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 個字元長的字元串。
這是一個簡單的部分解決方案,它僅適用於沒有出現在要搜尋的字元串(或正則表達式)中的字元,但**在行上出現的頻率足夠高,**因此該字元出現之間的間隔始終適合記憶體。例如,假設每一行都是一個很長的列表,由相對較短的分號分隔的欄位組成。
<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'
請注意,如果字元串碰巧幾乎存在,但中間有換行符,這可能會產生誤報。