【问题标题】:Merging two files with a key -> values structure [duplicate]使用键合并两个文件->值结构[重复]
【发布时间】:2012-04-28 10:17:40
【问题描述】:

我正在寻找想法,而不是 shell(linux) 中下一个问题的完整解决方案。什么是最好的解决方案? (awk, while-loop, sed ....)

我有两个具有相同行结构的文件:key-value-value。我想合并这两个文件。如果该值不存在,则脚本插入一个新行。如果存在,则脚本会更新值(通过将它们相加)。

Example:
File 1:

john-15-40
doo-10-91
mary-14-19
foo-11-0

File 2:

foo-110-10
john-22-11
ghost-1000-1000

Result:
foo-121-10
john-37-51
ghost-1000-1000
doo-10-91
mary-14-19

我该怎么做?

【问题讨论】:

    标签: bash shell sed awk grep


    【解决方案1】:

    简单的 awk

    awk '
      BEGIN {FS = OFS = "-"}
      {v1[$1] += $2; v2[$1] += $3}
      END {for (key in v1) {print key, v1[key], v2[key]}}
    ' F1 F2
    

    【讨论】:

      【解决方案2】:

      您需要一种带有辅助数组的语言。对于任何脚本语言,您的任务都非常简单,但 perl 和 awk 特别适合逐行处理文本文件。

      伪代码:

      read line from file1, file2
      split line to key and values
      if there are no key in hash
           add key and values
      else
           add values and print key/values
      

      【讨论】:

      • 这是一个很好的解决方案,谢谢,但不是 shell(linux) 中最聪明的,
      • @hunor 实际上,Bash 4 确实具有本机关联数组;如果您不需要它为旧版本的语言工作,这可以在本机 shell 中完成。
      • @Andrey 不,如果您在处理之前对输入流进行排序,则不需要关联数组。
      • @Charles 是的,谢谢。
      【解决方案3】:

      我知道你没有在 PHP 中要求它,但它可能会有所帮助。如果您愿意,可能还有其他语言中的类似内容:

      <?PHP
      
      $file_handle = fopen("file1", "r");
      
      while (!feof($file_handle) ) {
      $line_of_text = fgets($file_handle);
      list($name,$value1,$value2) = explode('-', $line_of_text);
      $file1[$name]=array($value1,$value2);
      }
      fclose($file_handle);
      // repeate for file2
      //then use the 2 arrays, $file1[] and $file2[] to rewrite the file as 'file3' or whatever. 
      //Checking for duplicates and doing the math.
      ?>
      

      【讨论】:

      • 感谢您的解决方案,我喜欢 php,它类似于 Andrey Yazu 伪代码
      • 我也是这么想的。他在我写作的时候发布了它。
      • 非常感谢,但我正在寻找基于 shell(linux) 的智能解决方案
      【解决方案4】:

      我喜欢 glenn 的 short fat 解决方案。还有一个tall thin 解决方案。

      如果您有两个文件:1.txt2.txt

      sort {1,2}.txt |
      awk -F- -vOFS=- '
      NR==1{
          x=$1
      }
      x==$1{
          y+=$2
          z+=$3
          next
      }
      {
          print x,y,z;
          x=$1
          y=$2
          z=$3
      }
      END{
          print
      }'
      

      【讨论】:

        【解决方案5】:

        这可以在 Bash 4 中本地完成:

        #!/bin/bash
        declare -A vals_one vals_two
        while IFS=- read key val1 val2; do
          if [[ ${vals_one["$key"]} ]] ; then
            vals_one["$key"]=$(( ${vals_one["$key"]} + val1 ))
            vals_two["$key"]=$(( ${vals_two["$key"]} + val2 ))
          else
            vals_one["$key"]=$val1
            vals_two["$key"]=$val2
          fi
        done < <(cat input1.txt input2.txt)
        for key in "${!vals_one[@]}"; do
          printf '%s-%s-%s\n' "$key" "${vals_one[$key]}" "${vals_two[$key]}"
        done
        

        请注意,这种方法有些内存效率低下;一种更节省内存的方法会在合并文件之前对文件进行排序(如果它排序的内容无法放入内存,GNU sort 能够生成临时文件,因此比我们编写的任何合理脚本都更有能力),因此只需要在内存中一次存储两行:

        #!/bin/bash
        
        function merge_inputs {
            IFS=- read key val1 val2
            while IFS=- read new_key new_val1 new_val2; do
              if [[ $key = "$new_key" ]] ; then
                val1=$(( val1 + new_val1 ))
                val2=$(( val2 + new_val2 ))
              else
                printf '%s-%s-%s\n' "$key" "$val1" "$val2"
                key=$new_key
                val1=$new_val1
                val2=$new_val2
              fi
            done
            printf '%s-%s-%s\n' "$key" "$val1" "$val2"
        }
        sort input1.txt input2.txt | merge_inputs
        

        另外,后一种形式不需要关联数组,并且可以与旧版本的 bash 一起使用(或者,经过一些调整,其他 shell)。

        【讨论】:

        • 感谢您的工作:d,我喜欢它,很好
        猜你喜欢
        • 1970-01-01
        • 2021-12-06
        • 2015-06-30
        • 2022-01-13
        • 2020-12-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多