【问题标题】:find common elements in >2 files在 >2 个文件中查找共同元素
【发布时间】:2013-05-31 21:59:50
【问题描述】:

我有如下三个文件

file1.txt

"aba" 0 0 
"aba" 0 0 1
"abc" 0 1
"abd" 1 1 
"xxx" 0 0

file2.txt

"xyz" 0 0
"aba" 0 0 0 0
"aba" 0 0 0 1
"xxx" 0 0
"abc" 1 1

file3.txt

"xyx" 0 0
"aba" 0 0 
"aba" 0 1 0
"xxx" 0 0 0 1
"abc" 1 1

我想根据前两列在所有三个文件中找到相似的元素。为了在两个文件中找到相似的元素,我使用了类似

awk 'FNR==NR{a[$1,$2]++;next}a[$1,$2]' file1.txt file2.txt 

但是,当输入文件超过 2 个时,我们如何在所有文件中找到相似的元素? 有人可以帮忙吗?

使用当前的 awk 解决方案,输出会忽略重复的键列并给出输出为

"xxx" 0 0

如果我们假设输出来自 file1.txt,那么预期的输出是:

"aba" 0 0 
"aba" 0 0 1
"xxx" 0 0 

即它也应该获取具有重复键列的行。

【问题讨论】:

    标签: awk compare


    【解决方案1】:

    尝试以下针对 N 个文件的通用解决方案。它将第一个文件的数据保存在值为1 的哈希中,并且对于来自下一个文件的每次命中,该值都会递增。最后,我比较每个键的值是否与处理的文件数相同,并仅打印匹配的文件。

    awk '
        FNR == NR { arr[$1,$2] = 1; next }
        { if ( arr[$1,$2] ) { arr[$1,$2]++ } }
        END { 
            for ( key in arr ) {
                if ( arr[key] != ARGC - 1 ) { continue }
                split( key, key_arr, SUBSEP )
                printf "%s %s\n", key_arr[1], key_arr[2] 
            } 
        }
    ' file{1..3}
    

    它产生:

    "xxx" 0
    "aba" 0
    

    EDIT 添加打印整行的版本(请参阅 cmets)。我在保存行的位置添加了另一个具有相同键的数组,并在printf 函数中使用它。我已将旧代码注释掉。

    awk '
        ##FNR == NR { arr[$1,$2] = 1; next }
        FNR == NR { arr[$1,$2] = 1; line[$1,$2] = $0; next }
        { if ( arr[$1,$2] ) { arr[$1,$2]++ } }
        END { 
            for ( key in arr ) {
                if ( arr[key] != ARGC - 1 ) { continue }
                ##split( key, key_arr, SUBSEP )
                ##printf "%s %s\n", key_arr[1], key_arr[2] 
                printf "%s\n", line[ key ] 
            } 
        }
    ' file{1..3}
    

    NEW EDIT(参见 cmets)添加一个使用相同键处理多行的版本。基本上我加入所有条目而不是只保存一个,将line[$1,$2] = $0 更改为line[$1,$2] = line[$1,$2] ( line[$1,$2] ? SUBSEP : "" ) $0。在打印时,我使用分隔符(SUBSEP 变量)进行反向拆分并打印每个条目。

    awk '
        FNR == NR { 
            arr[$1,$2] = 1
            line[$1,$2] = line[$1,$2] ( line[$1,$2] ? SUBSEP : "" ) $0
            next
        }
        FNR == 1 { delete found }
        { if ( arr[$1,$2] && ! found[$1,$2] ) { arr[$1,$2]++; found[$1,$2] = 1 } }
        END { 
            num_files = ARGC -1 
            for ( key in arr ) {
                if ( arr[key] < num_files ) { continue }
                split( line[ key ], line_arr, SUBSEP )
                for ( i = 1; i <= length( line_arr ); i++ ) { 
                    printf "%s\n", line_arr[ i ]
                } 
            } 
        }
    ' file{1..3}
    

    对有问题的新数据进行编辑,它会产生:

    "xxx" 0 0
    "aba" 0 0 
    "aba" 0 0 1
    

    【讨论】:

    • @user1779730:使用find 选择您要处理的所有文件,例如:awk '...' $(find /your/dir -maxdepth 1 -type f -name '*your_pattern*' -print) 或任何您需要的选择。
    • @user1779730:我不明白你的意思。这不是awk 决定您要处理哪些文件的问题。您想自动确定文件的数量,但基于什么?
    • 代码应该在目录中存在的任何数量的模式 *.txt 文件上运行
    • 您好,上面的代码只输出了两个关键列。如果键列相同,是否可以修改以从其中一个文件中获取所有列?即在上面的例子中,输出应该是“xxx”0 0和“aba”0 0。
    • 感谢它按预期工作。而且我怀疑,如果每个文件中的列数不相等,相同的代码是否可以正常工作?
    【解决方案2】:

    这个python脚本会列出所有文件的共同行:

    import sys
    i,l = 0,[]
    for files in sys.argv[1:]:
      l.append(set())
      for line in open(files): l[i].add(" ".join(line.split()[0:2]))
      i+=1
    commonFields =  reduce(lambda s1, s2: s1 & s2, l)
    for files in sys.argv[1:]:
      print "Common lines in ",files
      for line in open(files):
        for fields in commonFields:
          if fields in line:
            print line,
            break
    

    用法:python script.py file1 file2 file3 ...

    【讨论】:

    • 感谢您的代码。如果它是一个 awk 脚本对我会更有帮助
    【解决方案3】:

    对于三个文件,您只需要:

    awk 'FNR==NR { a[$1,$2]; next} ($1,$2) in a' file1.txt file2.txt file3.txt
    

    FNR==NR 块仅对参数列表中的第一个文件返回 true。此块中的next 语句强制跳过剩余的代码。因此,对参数列表中除第一个文件之外的所有文件执行($1,$2) in a。要以您拥有的方式处理更多文件,您需要做的就是列出它们。


    如果您需要更强大的命令行通配符,请使用extglob。您可以使用shopt -s extglob 将其打开,并使用shopt -u extglob 将其关闭。例如:

    awk 'FNR==NR { a[$1,$2]; next} ($1,$2) in a' file1.txt !(file1.txt)
    

    如果您很难找到文件,请使用find。例如:

    awk 'FNR==NR { a[$1,$2]; next} ($1,$2) in a' file1.txt $(find /path/to/files -type f -name "*[23].txt")
    

    我假设您正在寻找“N”个文件的全局范围。例如:

    awk 'FNR==NR { a[$1,$2]; next} ($1,$2) in a' file1.txt file{2,3}.txt
    

    【讨论】:

    • 谢谢。这里我提到了 3 个文件,但总的来说我会有 n 个文件。我们如何将其推广到 n 个文件
    • @user1779730:请参阅上面的评论。 HTH。
    • @user1779730:到底什么不起作用?我已经测试了代码,但如果您需要的输出与您在问题中描述的不同,则需要更具体。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-26
    • 1970-01-01
    • 1970-01-01
    • 2021-04-03
    • 2021-09-21
    • 2021-06-06
    • 1970-01-01
    相关资源
    最近更新 更多