4

我有 2 个文本文件。file1包含一个 ID 列表:

11002
10995
48981
79600

file2

10993   item    0
11002   item    6
10995   item    7
79600   item    7
439481  item    5
272557  item    7
224325  item    7
84156   item    6
572546  item    7
693661  item    7
.....

我正在尝试选择file2ID(第一列)所在的所有行file1。目前,我正在做的是遍历第一个文件以创建一个正则表达式,如:

^\b11002\b\|^\b10995\b\|^\b48981\b|^\b79600\b

然后运行:

grep '^11002\|^10995\|^48981|^79600' file2.txt

但是当 ID 的数量file1太大(~2000)时,正则表达式会变得很长并且grep变慢。还有其他方法吗?我正在使用 Perl + Awk + ​​Unix。

4

7 回答 7

6

使用哈希表。它可能是内存密集型的,但查找是在恒定时间内进行的。这是一个高效且正确的过程——不是唯一的,而是高效且正确的——用于创建哈希表、file1用作键以及file2在哈希表中查找键。如果键在哈希表中,则该行将打印到标准输出:

#!/usr/bin/env perl

use strict;
use warnings;

open FILE1, "< file1" or die "could not open file1\n";
my $keyRef;
while (<FILE1>) {
   chomp;
   $keyRef->{$_} = 1;
}
close FILE1;

open FILE2, "< file2" or die "could not open file2\n";
while (<FILE2>) {
    chomp;
    my ($testKey, $label, $count) = split("\t", $_);
    if (defined $keyRef->{$testKey}) {
        print STDOUT "$_\n";
    }
}
close FILE2;

在 Perl 中有很多方法可以做同样的事情。也就是说,我重视清晰和明确而不是花哨的晦涩,因为您永远不知道何时必须回到 Perl 脚本并进行更改,而且它们很难管理,因为它是。一个人的看法。

于 2012-12-05T21:03:19.993 回答
4
awk 'NR==FNR{tgts[$1]; next} $1 in tgts' file1 file2

看:

$ cat file1
11002
10995
48981
79600
$ cat file2
10993   item    0
11002   item    6
10995   item    7
79600   item    7
439481  item    5
272557  item    7
224325  item    7
84156   item    6
572546  item    7
693661  item    7
$ awk 'NR==FNR{tgts[$1]; next} $1 in tgts' file1 file2
11002   item    6
10995   item    7
79600   item    7
于 2012-12-05T21:13:29.743 回答
3

我建议使用专门为此而设计的工具。使用连接命令。做“男人加入”以获取更多信息。

linux_prompt> join file1 file2
11002 item 6
10995 item 7
79600 item 7
于 2012-12-05T21:38:17.587 回答
2

使用grep

$ grep -f f1 f2
11002   item    6
10995   item    7
79600   item    7

注意:我在多个系统上测试了很多建议的答案,有些只显示最后一场比赛79600 item 7!?

于 2012-12-05T20:59:54.977 回答
1

将第一个文件的所有元素加载到哈希中。对于第二个文件的每一行,^(\d*) 如果哈希包含提取的数字,则使用正则表达式提取数字,打印它

于 2012-12-05T21:04:53.837 回答
0

使用进程替换将 file1 中的 ID 转换为正则表达式:

grep -f <(sed 's/.*/^&\\b/' file1) file2

我假设您正在使用 bash 或类似功能的外壳

于 2012-12-05T22:07:38.063 回答
0

简单的 perl 解决方案是使用散列并计算寻找的数字的出现次数。

perl -lanwe 'print if $a{$F[0]}++ == 1;' file1.txt file2.txt

我从您的示例数据中得到以下输出:

11002   item    6
10995   item    7
79600   item    7

请注意,您需要在命令行中以正确的顺序使用这些文件。

这将打开并读取输入文件名-n(更改为.-a@F== 1>= 1

Note that the ++ operator is applied after the equality comparison is done.

于 2012-12-05T22:50:20.757 回答