【问题标题】:Fetch lines from a file based on line numbers specified in another file (preferably using awk)根据在另一个文件中指定的行号从文件中获取行(最好使用 awk)
【发布时间】:2014-02-19 04:33:27
【问题描述】:

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

numbers.txt:

1
5
1
4
2
20
1
...

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

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,但它不满足注意事项。

【问题讨论】:

  • 你会接受使用 Python 的 linecache 模块的答案吗?考虑到行号可以重复,缓存行似乎是明智的。
  • @kojiro 是的。只要我可以从 shell 运行它并在合理的时间内创建输出文件。虽然,我有 0 的 Python 经验。

标签: bash unix awk


【解决方案1】:

这几乎就是 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.

您也可以将其作为单行者执行此操作:

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

【讨论】:

  • 这很好用。谢谢!然而,黑井的回答做得稍微好一点。我多次运行这两种解决方案,awk 稍微快一点。两个峰值消耗的内存都在 150M 左右。一些统计数据: 您的解决方案:真正的 1m48.343s 用户 2m8.374s sys 0m43.307s Kuroi 的解决方案:真正的 1m40.651s 用户 2m1.133s sys 0m42.438s 对于大小约为 525MB 的文件。虽然赞成。再次感谢。
  • 确实是边际。这是一场比赛吗?尝试预加载行号以获得一点提升:python -c 'import linecache;print "\n".join(linecache.getline("data.txt", line) for line in map(int, open("numbers.txt").read().split()))' :)
【解决方案2】:

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

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

重新打开数据文件以一次获取一行会慢得多,即使使用磁盘缓存也是如此。

因此,我认为您可以放心地寻求将整个数据文件提取到内存中的解决方案。

这是我在 awk 中的做法:

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

有了这个输入

数据.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

(我在您的示例数据前添加了一个索引进行测试)。

数字.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。

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

【讨论】:

  • 非常感谢。如果您有兴趣,请参阅对 kojiro 的回答的评论以获取统计信息。再次感谢。
  • 哈哈,这就是我所说的基准。我想拥有一台和你一样快的电脑!我猜 python 和 awk 做的事情基本相同,只是 awk 是一个更紧凑和更专业的工具,这可能是它稍微领先的原因。
猜你喜欢
  • 2017-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-16
  • 2022-01-12
  • 1970-01-01
  • 2018-07-22
  • 2023-01-20
相关资源
最近更新 更多