1

我有 2 个文件
file1

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

和文件2

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 中做到这一点。如果可能的话,一个班轮会很有帮助。

谢谢。

4

2 回答 2

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

但我认为这里的关键是性能。所以试试吧。

于 2013-10-28T01:23:07.257 回答
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需要根据要比较的键对输入文件进行排序。请注意,这会生成完整的跨产品联接,这似乎不是您想要的。

注意:以下是一个非常依赖外壳的解决方案,但您可以很容易地将其转换为任何具有动态数组和内置排序原语的编程语言。不幸的是,awk 不是其中之一,但 perl 和 python 是,我敢肯定几乎每一种较新的脚本语言。)

似乎您真的希望密钥的每个实例在第一次在任何输出中发出时都被使用。您可以按如下方式获得它,再次从 and 的原始内容file1开始file2

$ 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)

于 2013-10-28T02:24:56.793 回答