如何使用awk或perl根据密钥交换多个列的值?

时间:2013-02-05 20:21:40

标签: perl awk key

是否有一种简单的方法可以在列表文件中交换列?

键是第一列和第三列,但第二列应附加到第一列,第四列应附加到第三列。 列之间的交换取决于第一个键(第一个列)与第二个键(第三列)的存在。

A B C D    
E F A B
H I A G
J K L M 
N J Q K

所需的输出如下:

A B C D    
A B E F
A G H I 
J K L M 
N J Q K

2 个答案:

答案 0 :(得分:1)

这适合你:我可以用“单行”制作,但我认为以这种方式粘贴更容易阅读。

awk 'NR==1{a[$1];print;next;}!($1 in a){
r="";h=$1;
for(i=2;i<=NF;i++)
    if($i in a){
        for(m=i;m<=NF;m++)
            r=(r?r" ":"")$m
            break;
        }else{
        h=h" "$i
    }
    $0=(r?r" ":"")h;
}1' file

使用您的数据进行测试:

kent$ echo "A B C D    
E F A B
H I A G
J K L M 
N J Q K"|awk 'NR==1{a[$1];print;next;}!($1 in a){
r="";h=$1;
for(i=2;i<=NF;i++)
        if($i in a){
                for(m=i;m<=NF;m++)
                        r=(r?r" ":"")$m
                        break;
                }else{
                h=h" "$i
        }
        $0=(r?r" ":"")h;
}1'
A B C D    
A B E F
A G H I
J K L M
N J Q K

答案 1 :(得分:1)

这是使用Perl的更简单的解决方案。如果同一行上的两个奇数列具有相同的内容,则会失败,例如

C D C A # Would print out "C A" only

否则,我们可以使用哈希数据结构来轻松操作键值对。

perl -ple'%h=split;$_=join" ",map{$_=>$h{$_}}sort keys %h'

示例用法:

$ perl -ple'%h=split;$_=join" ",map{$_=>$h{$_}}sort keys %h' <<'END'
A B C D    
E F A B
H I A G
J K L M 
N J Q K
END

输出:

A B C D
A B E F
A G H I
J K L M
N J Q K

没有那个弱点,我会像

那样写
perl -pale'@f=();push@f,[splice@F,0,2]while@F;$_=join" ",map@$_,sort{$a->[0]cmp$b->[0]}@f'

基本上是Schwartzian变换。

说明

第一个解决方案:

选项-l为我们处理行结尾。 -p遍历所有输入行(将它们放在$_中)并在每次迭代后打印出$_的内容。

哈希被构造为交替键和值的列表。没有选项的split函数在空格上拆分$_的内容,并返回我们分配给散列%h的列表。删除重复的密钥;只设置了最后一次出现。

我们sort按字母顺序排列keysmap获取每个键并将键列表转换为交替键和值的列表,但这次按正确顺序。

我们通过单个空格join这个字符串列表并将其分配给$_,由于-p而打印出来。

第二个解决方案:

-a选项自动split $_进入@F数组。我们将@F的前两个元素与splice一起使用,将它们放入匿名arrayref中,并将此arrayref推送到@f数组中。我们重复,直到没有留下任何一个。这会将@F的内容配对,而不会受到重复项的影响。

我们sort @f中的arrayrefs按字母顺序排列第一个元素,并使用map将结果顺序展平。之后,我们像以前一样加入字符串。