【问题标题】:best way to quickly filter lines of a huge 69GB text file according to multiple patterns根据多种模式快速过滤一个巨大的 69GB 文本文件的行的最佳方法
【发布时间】:2015-12-06 20:34:20
【问题描述】:

过滤文件中与约 20,000 个组合之一匹配的行的最佳方法是什么?模式是 \tZZZZ\t 其中 ZZZZ 是我创建的列表中的数字。

我一直在使用:

for i in `cat patterns.txt`;
do
echo "$i
cat large_file.txt | LC_ALL=C grep $i >> matched_lines.txt 
done

但它似乎很慢。 Python 实现也非常慢...

大文件如下所示(制表符分隔,3 列):

31599   94722   0.0184520931023
31599   33175   0.021944980284
31599   95587   0.0181413842575
31599   93637   0.0184741548464
31599   32411   0.0122635750533
31599   55509   0.0145808169111

【问题讨论】:

  • 您应该为一些模式提供一个示例,并向我们展示一些可以在您的大文件中找到的行
  • 模式可以表示为一组正则表达式吗? (.*)((pattern1)|(pattern2)|...)(.*) 然后在每一行上做一个正则表达式匹配?
  • 大文件看起来像上面的编辑,所有模式都从第二列中选择某些数字(文件是制表符分隔的)...
  • @sanchitarora 我不认为这些模式可以这样表示,因为它们本质上都是从第二列中选择一组给定的数字......
  • 来自 grep 手册页:“fgrep 比 grep 和 egrep 都快,但只能处理固定模式(即它不解释正则表达式)。”也许您想要“fgrep -f patterns.txt large_file.txt >matched_lines.txt”之类的东西?

标签: python grep


【解决方案1】:

使用 GNU sed 和 bash:

sed -n -f <(sed -n 's|.*|/\t&\t/p|p' patternfile.txt) largefile.txt > result.txt

使用 GNU sed、grep 和 bash:

grep -f <(sed -n 's|.*|\t&\t|p' patternfile.txt) largefile.txt > result.txt

【讨论】:

  • 谢谢,它仍然非常慢...也许我应该将大文件分成较小的文件?
  • 好的,分割成更小的文件并尝试几个并行会话。
【解决方案2】:

我遇到了类似的挑战,我已经确定了需要在包含数百万行的文件中匹配的数百万个模式。在我的例子中,模式是从搜索文件中识别出我需要的记录/行的字符串。

模式文件是大约 550 万行模式,304MiB。
要搜索的文件是 750 万行 json 文档(每行一个 json 文档),25.1GiB。

所以根据我的计算,这个问题的循环迭代超过了 41 万亿次。

7,500,000 * 5,500,000 = 41,250,000,000,000.

我的逻辑是需要在 750 万行/记录的每一行中搜索 550 万次搜索。

模式文件中的模式是唯一的。所以,我有一个想法,每次匹配的模式都可以从下一次搜索迭代中丢弃,从而减少迭代次数。我尝试在 python 中实现一些东西,但它太慢了,即使我以后要并行化它。

如果有人能对我的逻辑/数学问题发表评论,我将不胜感激。

这是我想出的解决方案:

fgrep -f 是一个显而易见的选择,但大约 300 MiB / 550 万行的模式导致内核 OOM 杀手在程序的早期终止进程。

所以我使用GNU parallels 工具来帮助分块模式文件,如下所示:

# create 7MB chunks (~100k lines/patterns per chunk), pass each chunk to fgrep -f
time parallel --tmpdir /dev/shm --pipepart --block 7m \
-a ../missing-eventIds-comm-quoted.txt --files 'cat' |
parallel --tmpdir /dev/shm -j4 -L1 --line-buffer '
  fgrep -f {} < <(exec 3<> /dev/pts/3; openssl enc -aes-256-cbc -d \
  -pass env:SYMETRIC_KEY -in possible-missing-data.gz.enc |
  gunzip --stdout | pv -s7510130 --line-mode 2>&3 -N job{#} )
  # remove the chunk
  rm {}
' | gzip --stdout |
openssl enc -aes-256-cbc -salt -pass env:SYMETRIC_KEY > $matches.gz.enc

parallel--files 选项将输入分块到文件中,并在标准输出上输出文件名。因此,在第二个 parallel 中,{} 模板标记被替换为块文件名之一。注意rm {} 稍后清理块。在此调用中,parallel 搜索搜索文件 n 次,直到处理完所有块。

根据我的数据,这种方法完成了 750 万行文件的 46 次块迭代。我指定了-j4 4 个并行作业经过的时间约为 23 分钟。
该节点使用Intel(R) Xeon(R) Gold 6242 CPU @ 2.80GHz vCPU 和16 GB RAM

这种方法使fgrep -f 内存使用量保持正常。

澄清以下内容:

< <(exec 3<> /dev/pts/3; openssl enc -aes-256-cbc -d \
-pass env:SYMETRIC_KEY -in possible-missing-data.gz.enc |
gunzip --stdout | pv -s7510130 --line-mode 2>&3 -N job{#} )

这是一个进程替换的例子,但是exec 3&lt;&gt;呢?我有一个tty/dev/pts/3 上运行,我想监控读取搜索文件的进度。所以我在文件描述符 3 exec 3&lt;&gt; /dev/pts/3 上打开了这个 tty。我使用pv (pipe viewer) 来监控我的/dev/pts/3 会话的进度。这给了我每个并行进程的进度条和速度检查。这是可选的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-15
    • 2015-08-06
    • 2022-01-12
    • 1970-01-01
    • 2016-08-18
    • 2015-03-24
    相关资源
    最近更新 更多