根据另一个文件中指定的行号从文件中获取行(最好使用awk)

时间:2014-02-18 18:52:44

标签: bash unix awk

我有一个包含数字的非常大的文件(约500万行)。

numbers.txt:

1
5
1
4
2
20
1
...

我有另一个包含数据的文件(约1百万行)。

data.txt中:

1.000000 -1.072000 -1.000000
2.000000 -1.213000 1.009900
-1.210000 -1.043000 1.000000
-1.000000 -1.000000 -1.000000
1.000000 1.000000 -0.999999
...

numbers.txt包含data.txt文件的行号。 我需要输出一个文件,其中numbers.txt替换为data.txt中的相应行。因此,对于上面的示例,输出将如下所示:

1.000000 -1.072000 -1.000000
1.000000 1.000000 -0.999999
1.000000 -1.072000 -1.000000
-1.000000 -1.000000 -1.000000
2.000000 -1.213000 1.009900
...

我认为awk是正确的方法,但我无法弄清楚如何去做。

有两点需要注意:

  • 文件非常大,因此无法将所有内容都读入内存。
  • 文件必须保留其订单。排序不是一种选择。

我确实找到了这个question,但它不符合警告。

2 个答案:

答案 0 :(得分:2)

这几乎是为Python构建的linecache模块:

#!/usr/bin/env python

from linecache import getline

with open('numbers.txt') as lines:
  for line in lines: # Read each line from the lines file
    try:
      print getline('data.txt', int(line)) # Attempt to get and print that line from the data file
    except ValueError:
      pass # line did not contain a numeral, so ignore it.

您也可以将此作为oneliner:

python -c 'import linecache;print "\n".join(linecache.getline("data.txt", int(line)) for line in open("numbers.txt"))'

答案 1 :(得分:1)

只有数据文件必须保留在内存中,因此索引文件可以是任意大小。

如果您的数据文件是100万行,大约40个字符,那么它应该适合40 Mb,这对于普通PC来说是轻而易举的。

即使使用磁盘缓存,重新打开数据文件一次获取一行也会慢一些。

所以我认为你可以安全地找到一个将整个数据文件提取到内存中的解决方案。

以下是我将如何在awk中执行此操作:

gawk "{if(NR==FNR)l[NR]=$0; else print l[$1] }" data.txt numbers.txt

使用此输入

data.txt中

1 1.000000 -1.072000 -1.000000
2 2.000000 -1.213000 1.009900
3 -1.210000 -1.043000 1.000000
4 -1.000000 -1.000000 -1.000000
5 1.000000 1.000000 -0.9999991.000000 -1.072000 -1.000000
6 2.000000 -1.213000 1.009900
7 -1.210000 -1.043000 1.000000
8 -1.000000 -1.000000 -1.000000
9 1.000000 1.000000 -0.9999991.000000 -1.072000 -1.000000
10 2.000000 -1.213000 1.009900
11 -1.210000 -1.043000 1.000000
12 -1.000000 -1.000000 -1.000000
13 1.000000 1.000000 -0.9999991.000000 -1.072000 -1.000000
14 2.000000 -1.213000 1.009900
15 -1.210000 -1.043000 1.000000
16 -1.000000 -1.000000 -1.000000
17 1.000000 1.000000 -0.9999991.000000 -1.072000 -1.000000
18 2.000000 -1.213000 1.009900
19 -1.210000 -1.043000 1.000000
20 -1.000000 -1.000000 -1.000000

(我在您的样本数据前添加了一个索引用于测试)。

numbers.txt

1
5
1
4
2
20
1

它产生

1 1.000000 -1.072000 -1.000000
5 1.000000 1.000000 -0.9999991.000000 -1.072000 -1.000000
1 1.000000 -1.072000 -1.000000
4 -1.000000 -1.000000 -1.000000
2 2.000000 -1.213000 1.009900
20 -1.000000 -1.000000 -1.000000
1 1.000000 -1.072000 -1.000000

性能测试

我使用这个PHP脚本生成测试用例:

<?php
$MAX_DATA  = 1000000;
$MAX_INDEX = 5000000;

$contents = "";
for ($i = 0 ; $i != $MAX_DATA ; $i++) $contents .= ($i+1) . " " . str_shuffle("01234567890123456789012345678901234567890123456789") . "\n";
file_put_contents ('data.txt', $contents);

$contents = "";
for ($i = 0 ; $i != $MAX_INDEX ; $i++) $contents .= rand(1, $MAX_DATA) . "\n";
file_put_contents ('numbers.txt', $contents);

echo "done.";
?>

随机输入1M数据和5M索引,上面的awk脚本大约需要20秒才能在我的PC上产生结果。
数据文件大约是56 Mb,awk进程消耗了大约197 mb。

正如人们所料,处理时间大致与给定数据集的索引文件大小成比例。