如何在一个非常大且行很长的文件中搜索字符串?

如何在一个非常大且行很长的文件中搜索字符串?

原来是我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

答案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

这会很慢,因为它会多次读取输入文件(您想要查找的字符串中的每个字符一次,每次在字符串中读取该搜索字符串的大小),但它应该适用于任何大小的文件和任何大小的行,使用 GNU awk 进行多字符RSRT(未经测试):

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 个字符长的字符串时。

相关内容