【问题标题】:Find a number of a file in a range of numbers of another file在另一个文件的编号范围内查找一个文件的编号
【发布时间】:2014-07-18 22:13:10
【问题描述】:

我有这两个输入文件:

file1
1   982444
1   46658343
3   15498261
2   238295146
21  47423507
X   110961739
17  7490379
13  31850803
13  31850989

file2
1   982400  982480
1   46658345    46658350
2   14  109
2   5000    9000
2   238295000   238295560
X   110961739   120000000
17  7490200 8900005

这是我想要的输出:

Desired output:
1   982444
2   238295146
X   110961739
17  7490379

这就是我想要的:在 file2 的第 1 列中找到 file1 的第 1 列元素。如果数量相同,则取file1的第2列的数量,并检查它是否包含在file2的column2和3的数量范围内。如果包含,则在输出中打印 file1 的行。

也许理解起来有点混乱,但我正在尽力而为。我已经尝试了一些事情,但我离解决方案还很远,任何帮助都将不胜感激。请在 bash、awk 或 perl 中使用。

提前致谢,

【问题讨论】:

    标签: bash perl awk


    【解决方案1】:

    只使用awk。该解决方案不会重复循环通过file1

    #!/usr/bin/awk -f
    NR == FNR {
        # I'm processing file2 since NR still matches FNR
        # I'd store the ranges from it on a[] and b[]
        # x[] acts as a counter to the number of range pairs stored that's specific to $1
        i = ++x[$1]
        a[$1, i] = $2
        b[$1, i] = $3
        # Skip to next record; Do not allow the next block to process a record from file2.
        next
    }
    {
        # I'm processing file1 since NR is already greater than FNR
        # Let's get the index for the last range first then go down until we reach 0.
        # Nothing would happen as well if i evaluates to nothing i.e. $1 doesn't have a range for it.
        for (i = x[$1]; i; --i) {
            if ($2 >= a[$1, i] && $2 <= b[$1, i]) {
                # I find that $2 is within range. Now print it.
                print
                # We're done so let's skip to the next record.
                next
            }
        }
    }
    

    用法:

    awk -f script.awk file2 file1
    

    输出:

    1   982444
    2   238295146
    X   110961739
    17  7490379
    

    使用 Bash(4.0 或更高版本)的类似方法:

    #!/bin/bash
    
    FILE1=$1 FILE2=$2
    
    declare -A A B X
    
    while read F1 F2 F3; do
        (( I = ++X[$F1] ))
        A["$F1|$I"]=$F2
        B["$F1|$I"]=$F3
    done < "$FILE2"
    
    while read -r LINE; do
        read F1 F2 <<< "$LINE"
        for (( I = X[$F1]; I; --I )); do
            if (( F2 >= A["$F1|$I"] && F2 <= B["$F1|$I"] )); then
                echo "$LINE"
                continue
            fi
        done
    done < "$FILE1"
    

    用法:

    bash script.sh file1 file2
    

    【讨论】:

    • 哦,我认为这是正确的答案,因为@fedorqui,对于您的解决方案,我在输出中得到重复的行(不知道为什么)。
    • 你能解释一下代码吗?只是为了学习...谢谢!
    • @cucurbit 在第一个块NR == FNR { ... 中,代码将分配a 和b 中的范围。 .在某种程度上,它们像二维数组一样存储。这仅处理文件 2,因为它仅在 NR 和 FNR 匹配的第一个处理的文件中。下一个块是处理文件 1 的部分。它只是遍历范围并比较当前记录。如果匹配,它会打印并跳到下一条记录为next
    • @cucurbit 重复行?请使用正确的示例输入和所需的输出更新您的问题,就像当前的一样,它工作正常。
    • @fedorqui 他可能指的是相交的范围,如果一条线在两者上都匹配,则会导致重复打印。重复读取 file1 时会发生这种情况。
    【解决方案2】:

    让我们混合使用 bash 和 awk:

    while read col min max
    do
        awk -v col=$col -v min=$min -v max=$max '$1==col && min<=$2 && $2<=max' f1
    done < f2
    

    说明

    • 对于 file2 的每一行,读取最小值和最大值,以及第一列的值。
    • 给定这些值,在文件 1 中检查具有相同第一列并且是文件 2 指定范围内的第二列的那些行。

    测试

    $ while read col min max; do awk -v col=$col -v min=$min -v max=$max '$1==col && min<=$2 && $2<=max' f1; done < f2
    1   982444
    2   238295146
    X   110961739
    17  7490379
    

    【讨论】:

    • 谢谢@fedorqui,但“1 46658343”不应该在那里......“46658343”不在“46658345”和“46658350”之间,:S
    • 对不起@hek2mgl,这有点难以解释。我想要file1的行,它们包含在file2的col2和col3的数字范围内,如果两个文件的col1和col1相同。
    • @cucurbit 哦,我明白了...使用awk 'FNR==NR {b[$1]=$2; c[$1]=$3; next} $1 in b &amp;&amp; b[$1]&lt;=$2 &amp;&amp; $2&lt;=c[$1]' f2 f1 进行测试,但仍然存在问题,因为1 的第一列出现了两次。
    • 不错的@fedorqui +1
    • 效果很好!非常感谢,也感谢 cmets! :)
    【解决方案3】:

    纯 bash ,基于 Fedorqui 解决方案

    #!/bin/bash
    while read col_2 min max
    do
        while read col_1 val
        do
           (( col_1 == col_2 && ( min <= val && val <= max ) )) && echo $col_1 $val
        done < file1
    done < file2
    

    【讨论】:

      【解决方案4】:
      cut -d' ' -f1 input2 | sed 's/^/^/;s/$/\\s/' | \ 
          grep -f - <(cat input2 input1) | sort -n -k1 -k3 | \ 
          awk 'NF==3 { 
                  split(a,b,","); 
                  for (v in b) 
                      if ($2 <= b[v] && $3 >= b[v]) 
                          print $1, b[v]; 
                      if ($1 != p) a=""} 
               NF==2 {p=$1;a=a","$2}' 
      

      生产:

      X 110961739
      1 982444
      2 238295146
      17 7490379
      

      【讨论】:

        【解决方案5】:

        这是一个 Perl 解决方案。如果我用file2 构建散列,它可能会更快但不太简洁,但这应该没问题。

        use strict;
        use warnings;
        use autodie;
        
        my @bounds = do {
          open my $fh, '<', 'file2';
          map [ split ], <$fh>;
        };
        
        open my $fh, '<', 'file1';
        while (my $line = <$fh>) {
          my ($key, $val) = split ' ', $line;
          for my $bound (@bounds) {
            next unless $key eq $bound->[0] and $val >= $bound->[1] and $val <= $bound->[2];
            print $line;
            last;
          }
        }
        

        输出

        1   982444
        2   238295146
        X   110961739
        17  7490379
        

        【讨论】:

          猜你喜欢
          • 2018-05-14
          • 2012-08-25
          • 2013-02-10
          • 1970-01-01
          • 2014-04-29
          • 1970-01-01
          • 2011-11-14
          • 1970-01-01
          相关资源
          最近更新 更多