Awk:将2个文件中的数据与重复键匹配

时间:2013-10-28 00:52:01

标签: perl awk

我有2个文件
文件1

a^b=-123
a^3=-124
c^b=-129
a^b=-130

和file2

a^b=-523
a^3=-524
a^b=-530

我想使用'='作为分隔符来查找密钥并获得以下输出

a^b^-123^-523
a^b^-130^-530
a^3^-124^-524

当没有重复的密钥时,很容易在awk映射第一个文件并循环遍历第二个文件,但是,对于重复项,它有点困难。我试过这样的事情:

awk -F"=" '
   FNR == NR {
      arr[$1 "^" $2] = $2;
      next;
   }
   FNR < NR {
      for (i in arr) {
         match(i, , /^(.*\^.*)\^([-0-9]*)$/, , ar);
         if ($1 == ar[1]) {
            if ($2 in load == 0) {
               if (ar[2] in l2 == 0) {
                  l2[ar[2]] = ar[2];
                  load[$2] = $2;
                  print i "^" $2
               }
            }
         }
      }
   }
' file1 file2

这很好用,但是,这并不奇怪,它非常慢。在大约600K记录的文件上,它运行了4个小时。

是否有更好,更有效的方法在一行awk或perl中执行此操作。如果可能的话,一个班轮将是很大的帮助。

感谢。

2 个答案:

答案 0 :(得分:1)

尝试这个awk代码,看看它是否比你的更快:(它可能是一个单行,如果你加入所有行,但我认为格式化,它更容易阅读)

awk -F'=' -v OFS="^" 'NR==FNR{sub(/=/,"^");a[NR]=$0;t=NR;next}
{   s=$1
        sub(/\^/,"\\^",s)
        for(i=1;i<=t;i++){
                if(a[i]~s){
                        print  a[i],$2
                        delete a[i]
                        break
                        }
                }
        }' file1 file2

以您的示例为例,输出预期结果:

a^b^-123^-523
a^3^-124^-524
a^b^-130^-530

但我认为关键在于表现。试试看。

答案 1 :(得分:1)

您可能希望查看join命令,该命令与您在此处执行的操作非常相似,但会生成完整的数据库样式连接。例如,假设file1file2包含您在上面显示的数据,那么命令

$ sort -o file1.out -t = -k 1,1 file1
$ sort -o file2.out -t = -k 1,1 file2
$ join -t = file1.out file2.out

产生输出

a^3=-124=-524
a^b=-123=-523
a^b=-123=-530
a^b=-130=-523
a^b=-130=-530

排序是必要的,因为为了高效,join要求输入文件在被比较的键上排序。请注意,这会生成完整的跨产品联接,这似乎不是您想要的。

注意:以下是一个非常重的shell解决方案,但您可以使用动态数组和内置排序原语将其轻松地转换为任何编程语言。不幸的是,awk isn'除了perl和python之外,其中一个是,我确信几乎每种新的脚本语言。)

您似乎确实希望第一次在任何输出中发出密钥的每个实例消耗。您可以按照以下方式获取此内容,再次从file1file2的原始内容开始。

$ nl -s = -n rz file1 | sort -t = -k 2,2 > file1.out
$ nl -s = -n rz file2 | sort -t = -k 2,2 > file2.out

这将使用原始行号装饰每一行,以便我们稍后可以恢复原始订单,然后在键上对它们进行排序以进行连接。剩下的工作是一个简短的管道,我已经分解成多个块,所以我们可以解释它。

join -t = -1 2 -2 2 file1.out file2.out |

此命令连接键名,现在在字段2中,并发出类似于早期连接输出中显示的记录,除了每行现在包括在file1和file2中找到键的行号。接下来,我们想要重新建立原始算法将使用的搜索顺序,因此我们继续使用

进行管道
 sort -t = -k 2,2 -k 4,4 |

首先对file1行号进行排序,然后对file2行号进行排序。最后,我们需要有效地模拟一个特定密钥一旦被消耗就无法重复使用的假设,以消除原始连接输出中不需要的匹配。

awk '
   BEGIN { OFS="="; FS="=" }
   $2 in seen2 || $4 in seen4 { next }
   { seen2[$2]++; seen4[$4]++; print $1,$3,$5 }
'

这将忽略引用任一文件中先前扫描的键的每一行,否则将打印以下内容

a^b=-123=-523
a^3=-124=-524
a^b=-130=-530

即使是非常大的输入,这应该是统一有效的,因为排序是 O(n log n),其他一切都是 O(n)