计算大文件中的行数

Dna*_*iel 64 linux mapreduce

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

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

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

我希望解决方案像一线运行一样简单,就像wc -l解决方案一样,但不确定它是多么可行.

有任何想法吗?

P.P*_*.P. 95

尝试: sed -n '$=' filename

猫也是不必要的:wc -l filename以你现在的方式就足够了.

  • @Dnaiel如果我猜你会说你首先运行`wc -l filename`然后你运行`sed -n'$ ='filename`,那么在第一次运行时wc必须从磁盘读取所有文件因此它可以完全缓存在你可能比3Gb内存更大的位置上,所以`sed`可以在接下来更快地运行.我在拥有6Gb RAM的机器上使用4Gb文件自己进行了测试,但我确保文件已经在缓存中; 得分:`sed` - 0m12.539s,`wc -l` - 0m1.911s.所以`wc`快了6.56倍.重做实验但在每次运行之前清除缓存,它们都需要大约58秒才能完成. (31认同)
  • 这种使用 sed 的解决方案具有不需要行尾字符的额外优势。wc 计算行尾字符(“\n”),所以如果你在文件中有一行没有 \n,那么 wc 将返回 0。 sed 将正确返回 1。 (2认同)

lve*_*lla 12

您的限制速度因素是存储设备的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 &
Run Code Online (Sandbox Code Playgroud)

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

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

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


Nic*_*kin 8

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

find . -name '*.txt' | parallel 'wc -l {}' 2>/dev/null | paste -sd+ - | bc
Run Code Online (Sandbox Code Playgroud)

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

find . -name '*.xz' | parallel 'xzcat {} | wc -l' 2>/dev/null | paste -sd+ - | bc
Run Code Online (Sandbox Code Playgroud)


Pra*_*ari 7

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

time grep -c $ my_file.txt;
Run Code Online (Sandbox Code Playgroud)

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

time wc -l my_file.txt;
Run Code Online (Sandbox Code Playgroud)

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

time sed -n '$=' my_file.txt;
Run Code Online (Sandbox Code Playgroud)

实际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;
Run Code Online (Sandbox Code Playgroud)

真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()
Run Code Online (Sandbox Code Playgroud)

res1:org.joda.time.Seconds = PT15S


Pir*_*ooz 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"
Run Code Online (Sandbox Code Playgroud)

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

$HADOOP_HOME/bin/hadoop fs -cat <output_path>/* | paste -sd+ | bc
Run Code Online (Sandbox Code Playgroud)


Nic*_*ico 5

我知道这个问题已经有几年了,但是扩展Ivella 的最后一个想法,这个 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)
Run Code Online (Sandbox Code Playgroud)

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