在非常大的文件中计算单词出现次数(运行时内存不足)grep -o foo | wc -l

3

如何计算非常大的文件中的单词数量?

我认为整个文件都在一行上,这可能是下面其中一个答案指出的问题之一。

在这种情况下,我有一个1.7 GB的xml文件,并尝试快速计算其中的一些内容。

我找到了这篇文章Count number of occurrences of a pattern in a file (even on same line) ,该方法对我有效,但只适用于特定大小的文件。

在大约300 MB左右(即40,000次出现)时,它仍然能正常工作。

cat file.xml | grep -o xmltag | wc -l    

但当文件大小超过一定限制时,我会收到"内存不足"的提示。


3
你尝试过这个命令吗? grep -o 'xmltag' file.xml | wc -l 它的作用是在文件file.xml中查找所有的'xmltag'字符串,并计算出现次数。 - Avinash Raj
参考Raj的注意事项,您不应该在能够自行读取数据的程序中使用cat。这会使程序变得更加复杂并减慢其运行速度。 - Jotne
是的,你需要将文件分成块。请注意,这种分割可能是任意的,因此每个块会增加一个分割单词,所以你的字数可能会增加(块数-1)。 - Hot Licks
3个回答

3

http://lists.gnu.org/archive/html/parallel/2014-07/msg00009.html

例子:在n行文本中搜索m个正则表达式。

在大文件中搜索多个正则表达式的最简单解决方案是:

grep -f regexps.txt bigfile

或者如果正则表达式是固定字符串:

grep -F -f regexps.txt bigfile

有两个限制因素:CPU和磁盘I/O。 CPU很容易衡量:如果grep使用了超过90%的CPU(例如在运行top时),则CPU是限制性因素,并且并行化将加速此过程。如果不是,则磁盘I/O是限制性因素,根据磁盘系统的不同,可能更快或更慢地并行化。唯一确定的方法是测量。

如果CPU是限制性因素,则应对regexps进行并行化:

cat regexp.txt | parallel --pipe -L1000 --round-robin grep -f - bigfile

这将会为每个CPU启动一个grep进程,并且每个CPU只读取bigfile一次,但由于这是并行执行的,除了第一次外,所有读取都将缓存在RAM中。根据regexp.txt的大小,使用--block 10m而不是-L1000可能更快。如果regexp.txt太大而无法适应RAM,请删除--round-robin并调整-L1000。这将导致bigfile被读取多次。
某些存储系统在并行读取多个块时表现更好。对于某些RAID系统和某些网络文件系统来说是正确的。为了并行读取bigfile:
parallel --pipepart --block 100M -a bigfile grep -f regexp.txt

这将把 bigfile 分成 100MB 的块,并在每个块上运行 grep。为了并行化 bigfile 的读取和 regexp.txt 的正则匹配,可以使用 --fifo 参数将两者组合起来:

parallel --pipepart --block 100M -a bigfile --fifo cat regexp.txt \| parallel --pipe -L1000 --round-robin grep -f - {}

1
您的file.xml中有多少个换行符?如果有一行非常长,那么grep失败并显示“grep: memory exhausted”可能就是其中一个原因。
解决方法是在不影响结果的地方加入\n。例如,在每个</之前添加。
cat big.xml | perl -e 'while(sysread(STDIN,$buf, 32768)){ $buf=~s:</:\n</:; syswrite(STDOUT,$buf); }'

GNU Parallel可以将大文件分割成较小的块。同样,您需要找到不在匹配中心的良好切割位置。对于XML来说,一个好的位置通常是在“>”和“<”之间:
parallel -a big.xml --pipepart --recend '>' --recstart '<' --block 10M grep -o xmltag

更好的是代表记录结束的结束标签:

parallel -a big.xml --pipepart --recend '</endrecord>' --block 10M grep -o xmltag

请注意,--pipepart是一个相对较新的选项,因此您需要使用20140622版本或更高版本。

我相信整个文件只有1行。 - user985366

0
尝试使用GNU Parallel来进行如下操作... 它将把file.xml拆分成1MB(或最近的换行符附近)大小的块,并将每个块传递给一个CPU核心来运行grep。因此,它不仅应该工作,而且应该更快地工作:
parallel --pipe grep -o xmltag < file.xml | wc -l

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接