【问题标题】:How to replace all the values in one file with their corresponding names from another file如何用另一个文件中的对应名称替换一个文件中的所有值
【发布时间】:2017-01-11 12:22:57
【问题描述】:

我需要帮助将一个文件 (File-1.txt) 中的值(数字)替换为另一个文件 (File-2) 中的名称(字符串),该文件包含第一列中的值和第二列中的相应名称.两个文件如下:

文件-1.txt

(0, ':', [])
(1, ':', [18])
(2, ':', [20, 41])
(3, ':', [18])
(4, ':', [17])
(5, ':', [])
(6, ':', [])

文件-2.txt

1   ALA_A_87
2   THR_A_127
3   GLY_A_128
4   ILE_A_130
5   THR_A_166
6   THR_A_167

预期输出:

(ALA_A_87, ':', [THR_A_127])
(THR_A_127, ':', [ALA_A_87, GLY_A_128, ILE_A_130])
(GLY_A_128, ':', [THR_A_127, 5])
(ILE_A_130, ':', [THR_A_127])
(THR_A_166, ':', [GLY_A_128, THR_A_167])
(THR_A_167, ':', [THR_A_166])

我一直在尝试一些 awk 代码,但没有运气!! awk 代码如下:

awk -F, '
    BEGIN {
        subs="ALA_A_87 THR_A_127 GLY_A_128 ILE_A_130 THR_A_166 THR_A_167";
        split( subs, subs_arr );
    }
    NR == 1 { 
        print; 
        next 
    } 
    NR>1{
        for i in {1 2 3 4 5 6 }; {
            $i = subs_arr[ $i++ ];
        }
        print
    }
' File-1.txt

在这里,我提供了要在代码本身中替换的名称和值,但如果它可以作为外部文件 (File-2.txt) 提供,并将 File-1.txt 中的数字与File-2.txt 的第一列,并将 File-1.txt 中的数字替换为 File-2.txt 中显示的相应名称。帮我解决这个代码。

提前致谢

【问题讨论】:

  • 你的预期输出应该如何?
  • 我鼓励您不要为错误道歉,尤其是那些不会使帖子无效的错误。您应该进行更改(如果您认为需要)并继续前进。您的意见与其他人一样有效。
  • @EdMorton:我们真的必须费力地读完你对 OP 粗鲁的第一句话吗? “我觉得五行数据就可以了” 就足够了。
  • @EdMorton:我觉得你的评论是不必要的粗鲁和罗嗦,讽刺的是,当你评论 OP 的粗鲁和罗嗦时,我就这么说了。当你只需要指出数据可以被削减时,你为什么觉得需要讽刺?我的<strike> 标签是有效的 HTML,它在帖子中有效,但在 cmets 中无效,而且我没有看到其他方式来传达相同的意图。你的评论有效果,只是因为OP的谦虚,我的无效是因为你没有这种谦虚,而是选择了争论和反击而不是纠正你的错误。
  • @EdMorton:你是说 “我们真的需要遍历 50 行输入吗” 不是讽刺的意思吗?你似乎很方便地去掉了大写字母,这对我来说并不是一个真正的问题。我不再有兴趣回应你为自己的粗鲁辩护的努力。

标签: bash perl awk sed


【解决方案1】:

我将假设文件未排序,即 File-1.txt 中的行 m 匹配 File 中的行 n -2.txt,其中 m 可能不等于 n。 (在撰写本文时,您尚未提及文件是否已排序。)

解决此问题的一种方法是从 File-2.txt 构建散列,逐行读取 File-1.txt,然后使用替换值每行的哈希值。

Perl 中的示例

#!/usr/bin/perl
use strict;
use warnings;

my $filename1 = 'File-1.txt';
my $filename2 = 'File-2.txt';
my %hash;
my $fh;


open($fh, '<', $filename2) or die "Failed to open $filename2: $!";
while (<$fh>) {
  chomp;
  my ($key, $value) = split;
  $hash{$key} = $value if $value;
}
close $fh;


open($fh, '<', $filename1) or die "Failed to open $filename1: $!";
while (<$fh>) {
  s%^\(\K(\d+)%$hash{$1} // $1%e;
  print;
}
close $fh;

样本输出

(0, ':', [])
(ALA_A_87, ':', [18])
(THR_A_127, ':', [20, 41])
(GLY_A_128, ':', [18])
(ILE_A_130, ':', [17])
(THR_A_166, ':', [])
(THR_A_167, ':', [])
...skipped...
(LEU_D_391, ':', [30])
(ASN_D_399, ':', [8])
(TYR_D_402, ':', [])
(SER_D_404, ':', [8])
(NAO1, ':', [])

如果你想替换所有的数字,那就更简单了。替换这个替换:

s%^\(\K(\d+)%$hash{$1} // $1%e;

用这个:

s%(\d+)%$hash{$1} // $1%ge;

样本输出

(0, ':', [])
(ALA_A_87, ':', [GLY_B_235])
(THR_A_127, ':', [CYS_B_237, SER_D_310])
(GLY_A_128, ':', [GLY_B_235])
(ILE_A_130, ':', [ASP_B_234])
(THR_A_166, ':', [])
...skipped...
(ARG_D_325, ':', [ASN_B_399, LEU_A_194, VAL_B_396])
(LEU_D_391, ':', [SER_B_310])
(ASN_D_399, ':', [GLY_A_190])
(TYR_D_402, ':', [])
(SER_D_404, ':', [GLY_A_190])
(NAO1, ':', [])

您也可以将此替换应用于整个文件内容。


注意,我使用% 作为正则表达式分隔符而不是通常的斜杠,因为否则不可能在替换中使用// 运算符。

注意,原来的表达方式

s/^\((\d+)(.*)$/'(' . ( $hash{$1} || $1 ). $2/e;

不是最优的,因为:

  • (.*)$ 部分被不必要地处理了;
  • \K 选项的帮助下,第一个( 可能会保持不变;

由于使用了 OR 运算符,表达式不太正确。如果$hash 中不存在密钥,则$hash{$1}undefined。 // 运算符更合适,因为它仅在左侧操作数未定义时才返回右侧操作数。

【讨论】:

  • s/^\(\K(\d+)/ $hash{$1} || $1 /e; 以防你真的不需要$2
  • 感谢 Osmanov 提供代码,但我要求将 File-1.txt 的所有数字替换为 File-2.txt 中给出的相应名称。我知道,困惑来自我这边,我很抱歉没有发布预期的输出
  • @Asha,那就简单了,把s/^\((\d+)(.*)$/'(' . ( $hash{$1} || $1 ). $2/e;替换成s/(\d+)/$hash{$1} || $1/ge; 就行了
  • 为什么你的替换删除了整个行的其余部分,然后又放回去?
  • 您不必要地将 (.*)$ 添加到模式的末尾,这会匹配并删除 整个 字符串,然后通过将 $2 附加到替换文本。
【解决方案2】:

如果这是 file2 的第二部分要替换的名称

awk 'FNR==NR{n[$1]=$2;next}
    {
    h=$0;gsub( /\[.*/, "[", h)

    N=$0;gsub( /.*\[|].*/, "", N)
    NsS = split( N, aNs, /,[[:blank:]]*/)

    printf( "%s", h)
    for ( i=1; i<=NsS; i++) printf( "%s%s", (i==1 ? "" : ", "), n[aNs[i]])
    printf( "]\n")
    }
    ' File-2.txt File-1.txt

受到詹姆斯布朗速度和能力的挑战,第二个版本:-)

awk -F '[][[:blank:],]*' '
    FNR==NR{split($0,t,/[[:blank:]]*/);n[t[1]]=t[2];next}
    {
    printf( "%s, %s, [",$1, $2)
    for ( i=3; i<NF; i++) printf( "%s%s", (i==3 ? "" : ", "), n[$i])
    printf( "])\n")
    }
    ' File-2.txt File-1.txt

使用@JamesBrown 和最后的 OP 信息回复内部“挑战”(更改 F2 的所有编号)

awk -F '[^[:digit:]]*' '
    FNR==NR{n[$1]=$2;next}
    { for ( i=1; i<NF; i++) sub( $i, n[$i]) }
    7
    ' File-2.txt File-1.txt

【讨论】:

    【解决方案3】:

    我真的不确定应该替换哪些数字,但是这个替换了[] 中的数字,因为它感觉更有趣。您应该始终提供预期的结果:

    $ awk 'NR==FNR { a[$1]=$2; next }
           {
                b=$0;
                gsub(/^.*\[|\].*$/,"",b); 
                if(split(b,c,", ")) 
                    for(i in c) 
                        sub(c[i],a[c[i]],b);
                sub(/\[.*\]/,"[" b "]",$0)
           } 1' f2 f1
    (0, ':', [])
    (1, ':', [GLY_B_235])
    (2, ':', [CYS_B_237, SER_D_310])
    (3, ':', [GLY_B_235])
    (4, ':', [ASP_B_234])
    ...
    

    编辑:为@NeronLeVelu 添加了一个版本:

    $ awk 'NR==FNR { a[$1]=$2; next }
                   { for(i=2;++i<=NF;)
                         sub(/[0-9]+/,a[substr($i,match($i,/[0-9]+/),RLENGTH)],$i)
                   } 1' f2 f1
    (0, ':', [])
    (1, ':', [GLY_B_235])
    (2, ':', [CYS_B_237, SER_D_310])
    (3, ':', [GLY_B_235])
    (4, ':', [ASP_B_234])
    ...
    

    另一个编辑:在需求明确后(2->0),这是工作版本:

    $ awk 'NR==FNR { a[$1]=$2; next }
                   { for(i=0;++i<=NF;)
                         sub(/[0-9]+/,a[substr($i,match($i,/[0-9]+/),RLENGTH)],$i)
                   } 1' f2 f1
    (, ':', [])
    (ALA_A_87, ':', [GLY_B_235])
    (THR_A_127, ':', [CYS_B_237, SER_D_310])
    (GLY_A_128, ':', [GLY_B_235])
    (ILE_A_130, ':', [ASP_B_234])
    

    【讨论】:

    • 小心sub()s 使用输入数据作为正则表达式或替换,因为当输入包含正则表达式元字符(例如. * ? +)和/或反向引用字符(例如&amp;)时,它们会神秘地失败。
    • 当然。 &amp; 在 f2 中不存在,所以 a["&amp;"] 是一个空字符串,然后当您添加 &amp; 作为索引时,您也得到了它所映射的内容,foo
    • 哦,等一下...您是否试图将数组索引中的"&amp;" 解释为反向引用?不,您不能这样做,也不能将它用作函数sub(/X/,tolower("&amp;")) 等的参数,如您所愿。这是第三个参数匹配()的用途之一:match($0,/(.*)([0-9]+)(.*)/,b) { $0=b[1] a[b[2]] b[3]} 和@987654337 @
    • @ James Brown 和@ Ed Morton,我希望 File-1.txt 中的所有数字都被替换,即在 [ ] 内部和外部。我知道我在之前的帖子中并不清楚,但现在我已经编辑了这个问题以更清楚。对此我深表歉意
    • 哇,让我再看一遍 :-) ... 有一个新版本(具有更容易优化的新约束)
    【解决方案4】:

    在 Perl 中

    这会直接从File-2.txt 的内容构建散列%convert,并从其键构建正则表达式。然后它检查File-1.txt 的每一行是否有一对非空方括号,并将其中的所有数字转换为相应的哈希值

    use strict;
    use warnings 'all';
    use autodie;
    
    my %convert = do {
        open my $fh, '<', 'File-2.txt';
        map { split } <$fh>;
    };
    
    my $re = join '|', keys %convert;
    $re    = qr/\b(?:$re)\b/;
    
    open my $fh, '<', 'File-1.txt';
    
    while ( <$fh> ) {
        s/($re)/$convert{$1}/g;
        print;
    }
    

    输出

    (0, ':', [])
    (ALA_A_87, ':', [GLY_B_235])
    (THR_A_127, ':', [CYS_B_237, SER_D_310])
    (GLY_A_128, ':', [GLY_B_235])
    (ILE_A_130, ':', [ASP_B_234])
    (THR_A_166, ':', [])
    (THR_A_167, ':', [])
    (LYS_A_189, ':', [])
    (GLY_A_190, ':', [LEU_D_391])
    (ALA_A_191, ':', [])
    (GLY_A_192, ':', [ARG_B_304])
    (MET_A_193, ':', [ASP_B_241])
    (LEU_A_194, ':', [])
    (THR_B_200, ':', [SER_B_404])
    (MET_B_201, ':', [])
    (ASP_B_232, ':', [ASP_D_234])
    (ILE_B_233, ':', [ASN_B_399])
    (ASP_B_234, ':', [THR_B_200])
    (GLY_B_235, ':', [])
    (SER_B_236, ':', [])
    (CYS_B_237, ':', [LEU_D_311])
    (SER_B_238, ':', [GLN_B_305, ASN_B_240])
    (ASN_B_240, ':', [GLY_B_235, MET_A_193, GLN_B_305, ARG_B_304])
    (ASP_B_241, ':', [GLU_B_398])
    (ALA_B_279, ':', [])
    (GLU_B_280, ':', [])
    (ARG_B_304, ':', [ASP_B_241, GLY_A_128])
    (GLN_B_305, ':', [MET_A_193, VAL_B_396])
    (ARG_B_308, ':', [ASN_D_399])
    (ASP_B_309, ':', [THR_A_167])
    (SER_B_310, ':', [])
    (LYS_B_313, ':', [SER_B_238])
    (LEU_B_391, ':', [ARG_B_308])
    (TYR_B_395, ':', [MET_A_193, ARG_D_325, GLU_B_398, GLN_B_305])
    (VAL_B_396, ':', [])
    (GLU_B_398, ':', [ASP_B_309])
    (ASN_B_399, ':', [LYS_A_189])
    (SER_B_404, ':', [ARG_B_308])
    (GLY_C_192, ':', [])
    (ASP_D_234, ':', [ASP_B_309, LEU_D_391])
    (ASP_D_309, ':', [])
    (SER_D_310, ':', [ARG_D_325, LYS_A_189])
    (LEU_D_311, ':', [THR_D_314])
    (THR_D_314, ':', [ASN_B_240, GLY_B_235])
    (ARG_D_325, ':', [ASN_B_399, LEU_A_194, VAL_B_396])
    (LEU_D_391, ':', [SER_B_310])
    (ASN_D_399, ':', [GLY_A_190])
    (TYR_D_402, ':', [])
    (SER_D_404, ':', [GLY_A_190])
    (NAO1, ':', [])
    

    【讨论】:

    • 我很抱歉没有提供代码的预期输出。我希望 File-1 中的所有数字都替换为名称,而不管“:”的一侧
    • 那么在File-.txt 中将0 归零应该怎么办?这种方式要简单得多;我已经改变了我的答案,以便它留下没有出现在 `File-2.tx5 中的数字
    【解决方案5】:

    这个 awk 单行代码应该会有所帮助:

    awk 'NR==FNR{a[$1]=$2;next}{FS="[(,]"}sub(/[^,]*/,"("a[$2])+1' f2 f1
    

    它输出如下内容:

    (, ':', [])
    (ALA_A_87, ':', [18])
    (THR_A_127, ':', [20, 41])
    (GLY_A_128, ':', [18])
    (ILE_A_130, ':', [17])
    (THR_A_166, ':', [])
    (THR_A_167, ':', [])
    (LYS_A_189, ':', [])
    (GLY_A_190, ':', [45])
    (ALA_A_191, ':', [])
    (GLY_A_192, ':', [26])
    (MET_A_193, ':', [23])
    (LEU_A_194, ':', [])
    (THR_B_200, ':', [37])
    .....
    

    注意第一行,0在file2中不存在,所以()中有一个空,如果这不是你想要的,请告诉我什么价值应该在那里。

    【讨论】:

    • 肯特,如果代码的名称不存在,那么我不需要替换任何东西。为了更清楚,我发布了一个经过编辑的问题形式。抱歉给您添麻烦
    • @Asha 这将是一个微小的修改。只是检查$2 in a我仍然不明白反对票... :(因为它太短了?你已经有了解决方案?还是我应该更新答案?
    • 请不要询问人们投反对票的原因。如果他们打算的话,他们会向你解释的,而询问它几乎永远不会得到答案。投票是匿名的是有原因的,虽然在不被告知原因的情况下丢分可能会令人沮丧,但这就是它的工作方式。
    【解决方案6】:

    这可能对你有用(GNU sed):

    sed -r 's/^(\S+)\s+(\S+)$/s:\\b\1\\b:\2:g/' file2 | sed -rf - file1
    

    这会将文件 2 转换为充当文件 1 查找的 sed 脚本。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-27
      • 2020-12-17
      • 2012-02-28
      • 1970-01-01
      • 1970-01-01
      • 2019-03-06
      • 2018-03-29
      • 2016-07-12
      相关资源
      最近更新 更多