计算大文件中的行数

时间:2012-10-03 20:42:00

标签: linux mapreduce

我通常使用大约20 Gb的文本文件,我发现自己经常计算给定文件中的行数。

我现在这样做只是cat fname | wc -l,而且需要很长时间。有没有更快的解决方案?

我在安装了Hadoop的高性能群集中工作。我想知道地图减少方法是否有帮助。

我希望解决方案像一行一样简单,就像wc -l解决方案一样,但不确定它是否可行。

有什么想法吗?

14 个答案:

答案 0 :(得分:89)

尝试:sed -n '$=' filename

此外猫也是不必要的:wc -l filename就目前而言已经足够了。

答案 1 :(得分:11)

您的限制速度因素是您的存储设备的I / O速度,因此在简单的换行符/模式计数程序之间进行更改将无济于事,因为这些程序之间的执行速度差异可能会被较慢的磁盘所抑制/存储/无论你有什么。

但是如果您在磁盘/设备上复制了相同的文件,或者文件分布在这些磁盘中,您当然可以并行执行操作。我不是特别了解这个Hadoop,但假设您可以从4个不同的位置读取10gb的文件,您可以运行4个不同的行计数过程,每个过程在文件的一个部分中,并将它们的结果相加:

$ dd bs=4k count=655360 if=/path/to/copy/on/disk/1/file | wc -l &
$ dd bs=4k skip=655360 count=655360 if=/path/to/copy/on/disk/2/file | wc -l &
$ dd bs=4k skip=1310720 count=655360 if=/path/to/copy/on/disk/3/file | wc -l &
$ dd bs=4k skip=1966080 if=/path/to/copy/on/disk/4/file | wc -l &

注意每个命令行的&,所以所有命令都会并行运行; dd的工作方式与cat类似,但允许我们指定要读取的字节数(count * bs个字节)以及输入开头要跳过的字节数(skip * bs个字节)。它在块中工作,因此需要指定bs作为块大小。在这个例子中,我将4Gb文件划分为4个相等的4Kb * 655360 = 2684354560字节= 2.5GB的块,每个作业给出一个,你可能想要设置一个脚本来根据你的大小为你做这个。文件和您将运行的并行作业数。你还需要总结执行的结果,我没有为缺乏shell脚本能力做的事情。

如果您的文件系统足够聪明,可以在诸如RAID或分布式文件系统之类的许多设备之间拆分大文件,并自动并行化可以进行并行化的I / O请求,那么您可以执行此类拆分,运行多个并行作业,但使用相同的文件路径,你仍然可以有一些速度增益。

编辑: 我想到的另一个想法是,如果文件中的行具有相同的大小,则可以通过将文件大小除以行的大小(以字节为单位)来获得确切的行数。你可以在一份工作中几乎立即完成。如果你有平均尺寸并且不关心线数,但想要估计,你可以做同样的操作,并且比准确的操作更快地获得满意的结果。

答案 2 :(得分:8)

在多核服务器上,使用GNU parallel并行计算文件行。打印完每个文件行数后,bc将所有行计数相加。

find . -name '*.xz' | parallel 'xzcat {} | wc -l' 2>/dev/null | paste -sd+ - | bc

为了节省空间,您甚至可以压缩所有文件。以下行解压缩每个文件并并行计算其行,然后对所有计数求和。

{{1}}

答案 3 :(得分:6)

如果您的数据驻留在HDFS上,也许最快的方法是使用hadoop流。 Apache Pig的COUNT UDF在一个包上运行,因此使用一个reducer来计算行数。相反,您可以在简单的hadoop流式脚本中手动设置reducer的数量,如下所示:

$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar -Dmapred.reduce.tasks=100 -input <input_path> -output <output_path> -mapper /bin/cat -reducer "wc -l"

请注意,我手动将减速器数量设置为100,但您可以调整此参数。完成map-reduce作业后,每个reducer的结果都存储在一个单独的文件中。行的最终计数是所有reducer返回的数字的总和。您可以按如下方式获取最终行数:

$HADOOP_HOME/bin/hadoop fs -cat <output_path>/* | paste -sd+ | bc

答案 4 :(得分:5)

根据我的测试,我可以验证Spark-Shell(基于Scala)比其他工具(GREP,SED,AWK,PERL,WC)更快。这是我在一个有23782409行的文件上运行的测试结果

time grep -c $ my_file.txt;

真正的0m44.96s 用户0m41.59s sys 0m3.09s

time wc -l my_file.txt;

真正的0m37.57s 用户0m33.48s sys 0m3.97s

time sed -n '$=' my_file.txt;

真正的0m38.22s 用户0m28.05s sys 0m10.14s

time perl -ne 'END { $_=$.;if(!/^[0-9]+$/){$_=0;};print "$_" }' my_file.txt;

真正的0m23.38s 用户0m20.19s sys 0m3.11s

time awk 'END { print NR }' my_file.txt;

真实0m19.90s 用户0m16.76s sys 0m3.12s

spark-shell
import org.joda.time._
val t_start = DateTime.now()
sc.textFile("file://my_file.txt").count()
val t_end = DateTime.now()
new Period(t_start, t_end).toStandardSeconds()

res1:org.joda.time.Seconds = PT15S

答案 5 :(得分:3)

Hadoop本质上提供了一种机制来执行类似于@Ivella建议的操作。

Hadoop的HDFS(分布式文件系统)将采用您的20GB文件,并以固定大小的块将其保存在群集中。假设您将块大小配置为128MB,文件将被拆分为20x8x128MB块。

然后,您将对此数据运行map reduce程序,主要是计算每个块的行(在映射阶段),然后将这些块行计数减少为整个文件的最终行数。

就性能而言,一般来说,群集越大,性能越好(并行运行wc越多,独立磁盘越多),但作业编排中存在一些开销,这意味着在较小的文件上运行作业不会实际上比运行本地wc产生更快的吞吐量

答案 6 :(得分:3)

我知道这个问题现在已经有几年了,但是在Ivella's last idea扩展,这个bash脚本 估计 一个大文件的行数在几秒钟内或者通过测量一条线的大小并从中推断出来:

#!/bin/bash
head -2 $1 | tail -1 > $1_oneline
filesize=$(du -b $1 | cut -f -1)
linesize=$(du -b $1_oneline | cut -f -1)
rm $1_oneline
echo $(expr $filesize / $linesize)

如果您将此脚本命名为lines.sh,则可以调用lines.sh bigfile.txt来获取估计的行数。在我的情况下(大约6 GB,从数据库导出),与真实行数的偏差仅为3%,但运行速度提高了约1000倍。顺便说一下,我用第二行而不是第一行作为基础,因为第一行有列名,实际数据在第二行开始。

答案 7 :(得分:2)

我不确定python是否更快:

[root@myserver scripts]# time python -c "print len(open('mybigfile.txt').read().split('\n'))"

644306


real    0m0.310s
user    0m0.176s
sys     0m0.132s

[root@myserver scripts]# time  cat mybigfile.txt  | wc -l

644305


real    0m0.048s
user    0m0.017s
sys     0m0.074s

答案 8 :(得分:2)

如果你的瓶颈是磁盘,重要的是你如何阅读它。对于具有HDD和快速CPU和RAM的计算机,dd if=filename bs=128M | wc -lwc -l filenamecat filename | wc -l批次。您可以使用块大小,并查看dd报告的吞吐量。我把它调到1GiB。

注意:关于catdd是否更快,存在争议。我声称dd可以更快,取决于系统,它适合我。亲自试试。

答案 9 :(得分:1)

如果您的计算机有python,您可以在shell中尝试:

python -c "print len(open('test.txt').read().split('\n'))"

这使用python -c传递一个命令,该命令基本上是读取文件,然后按&#34;换行符&#34;分割,以获取换行符的计数或文件的总长度

@BlueMoon's

bash-3.2$ sed -n '$=' test.txt
519

使用上述内容:

bash-3.2$ python -c "print len(open('test.txt').read().split('\n'))"
519

答案 10 :(得分:1)

find -type f -name“filepattern_2015_07 _ * .txt”-exec ls -1 {} \; |猫| awk'// {print $ 0,system(“cat”$ 0“|”“wc -l”)}'

输出:

答案 11 :(得分:0)

让我们假设:

  • 您的文件系统已分发
  • 您的文件系统可以轻松地将网络连接填充到单个节点
  • 您可以像普通文件一样访问您的文件

然后你真的想把文件分成几部分,在多个节点上并行计算部分并总结那里的结果(这基本上是@Chris White的想法)。

以下是使用GNU Parallel(版本&gt; 20161222)的方法。您需要在~/.parallel/my_cluster_hosts中列出节点,并且必须ssh访问所有节点:

parwc() {
    # Usage:
    #   parwc -l file                                                                

    # Give one chunck per host                                                     
    chunks=$(cat ~/.parallel/my_cluster_hosts|wc -l)
    # Build commands that take a chunk each and do 'wc' on that                    
    # ("map")                                                                      
    parallel -j $chunks --block -1 --pipepart -a "$2" -vv --dryrun wc "$1" |
        # For each command                                                         
        #   log into a cluster host                                                
        #   cd to current working dir                                              
        #   execute the command                                                    
        parallel -j0 --slf my_cluster_hosts --wd . |
        # Sum up the number of lines                                               
        # ("reduce")                                                               
        perl -ne '$sum += $_; END { print $sum,"\n" }'
}

用作:

parwc -l myfile
parwc -w myfile
parwc -c myfile

答案 12 :(得分:0)

我有一个645GB的文本文件,并且之前的确切解决方案(例如wc -l)都没有在5分钟内返回答案。

相反,这是Python脚本,用于计算大型文件中的近似行数。 (我的文本文件显然有约55亿行。)Python脚本执行以下操作:

A。计算文件中的字节数。

B。读取文件中的前N行(作为示例),并计算平均行长。

C。将A / B计算为大约的行数。

它沿Nico's answer线行,但是它不计算一行的长度,而是计算前N行的平均长度。

注意:我假设使用ASCII文本文件,所以我希望Python len()函数将char数作为字节数返回。

将此代码放入文件line_length.py

#!/usr/bin/env python

# Usage:
# python line_length.py <filename> <N> 

import os
import sys
import numpy as np

if __name__ == '__main__':

    file_name = sys.argv[1]
    N = int(sys.argv[2]) # Number of first lines to use as sample.
    file_length_in_bytes = os.path.getsize(file_name)
    lengths = [] # Accumulate line lengths.
    num_lines = 0

    with open(file_name) as f:
        for line in f:
            num_lines += 1
            if num_lines > N:
                break
            lengths.append(len(line))

    arr = np.array(lengths)
    lines_count = len(arr)
    line_length_mean = np.mean(arr)
    line_length_std = np.std(arr)

    line_count_mean = file_length_in_bytes / line_length_mean

    print('File has %d bytes.' % (file_length_in_bytes))
    print('%.2f mean bytes per line (%.2f std)' % (line_length_mean, line_length_std))
    print('Approximately %d lines' % (line_count_mean))

使用N = 5000调用它。

% python line_length.py big_file.txt 5000

File has 645620992933 bytes.
116.34 mean bytes per line (42.11 std)
Approximately 5549547119 lines

文件中大约有55亿行。

答案 13 :(得分:0)

将较慢的 IO 回退到 dd if={file} bs=128M | wc -l 会在收集数据以供 wc 处理时提供极大帮助。

我也偶然发现

https://github.com/crioux/turbo-linecount

这很棒。