【问题标题】:Renaming names in a file using another file without using loops使用另一个文件重命名文件中的名称而不使用循环
【发布时间】:2012-07-03 03:27:43
【问题描述】:

我有两个文件:

(one.txt) 看起来像这样:

>ENST001 

(((....)))

(((...)))

>ENST002 

(((((((.......))))))

((((...)))

我还有 10000 个 ENST

(two.txt) 看起来像这样:

>ENST001   110

>ENST002  59

对于所有 ENST 的其余部分,依此类推

我基本上想用 (two.txt) 中的两个字段的组合替换 (one.txt) 中的 ENST,因此结果将如下所示:

>ENST001_110 

(((....)))

(((...)))

>ENST002_59 

(((((((.......))))))

((((...)))

我为此编写了一个 matlab 脚本,但由于它会循环 (two.txt) 中的所有行,因此需要 6 个小时才能完成,所以我认为使用 awk、sed、grep 甚至 perl 我们可以得到结果在几分钟内。这是我在matlab中所做的:

frf = fopen('one.txt', 'r');       
frp = fopen('two.txt', 'r');                                     
fw = fopen('result.txt', 'w');    

while feof(frf) == 0

line = fgetl(frf);
first_char = line(1);

if strcmp(first_char, '>') == 1 % if the line in one.txt start by > it is the ID 

   id_fold = strrep(line, '>', ''); % Reomve the > symbol


   frewind(frp)     % Rewind two.txt file after each loop

    while feof(frp) == 0

        raw = fgetl(frp);
        scan = textscan(raw, '%s%s');
        id_pos = scan{1}{1};
        pos = scan{2}{1};

            if strcmp(id_fold, id_pos) == 1  % if both ids are the same


                id_new = ['>', id_fold, '_', pos];

                fprintf(fw, '%s\n', id_new);

            end    

    end    

else

    fprintf(fw, '%s\n', line);  % if the line doesn't start by > print it to results



end  

结束

【问题讨论】:

    标签: perl matlab sed awk grep


    【解决方案1】:

    使用awk 的一种方式。 FNR == NR 处理参数中的第一个文件并保存每个数字。第二个条件处理第二个文件,当第一个字段与数组中的一个键匹配时,修改附加数字的那一行。

    awk '
        FNR == NR { 
            data[ $1 ] = $2; 
            next 
        } 
        FNR < NR && data[ $1 ] { 
            $0 = $1 "_" data[ $1 ] 
        } 
        { print }
    ' two.txt one.txt
    

    输出:

    >ENST001_110
    
    (((....)))
    
    (((...)))
    
    >ENST002_59
    
    (((((((.......))))))
    
    ((((...)))
    

    【讨论】:

      【解决方案2】:

      使用sed,您最初只能在two.txt 上运行,您可以根据需要创建sed 命令替换并在one.txt 上运行:

      第一种方式

      sed "$(sed -n '/>ENST/{s=.*\(ENST[0-9]\+\)\s\+\([0-9]\+\).*=s/\1/\1_\2/;=;p}' two.txt)" one.txt
      

      第二种方式

      如果文件很大,您将通过以前的方式获得too many arguments error。因此,还有另一种方法可以解决此错误。您需要一个一个地执行所有三个命令:

      sed -n '1i#!/bin/sed -f
      />ENST/{s=.*\(ENST[0-9]\+\)\s\+\([0-9]\+\).*=s/\1/\1_\2/;=;p}' two.txt > script.sed
      chmod +x script.sed
      ./script.sed one.txt
      

      第一个命令将形成 sed 脚本,该脚本将能够根据需要修改 one.txt。 chmod 将使这个新脚本可执行。最后一个命令将执行命令。所以每个文件只读取一次。没有任何循环。 请注意,第一个命令由两行组成,但仍然是一个命令。如果您删除换行符,它将破坏脚本。这是因为sed 中的i 命令。您可以在 ``sed 手册页中查找详细信息。

      【讨论】:

      • 是的,这就是为什么我写了关于通过文件运行的答案的第二部分。试试看,它应该可以正常工作。请注意,-f 后面必须有换行符。否则将无法正常工作。
      • 第二部分中的文件 one.txt 丢失
      • 不。它不是。好的,我已经更新了我的帖子以使其更加清晰。
      【解决方案3】:

      此 Perl 解决方案将修改后的 one.txt 文件发送到 STDOUT

      use strict;
      use warnings;
      
      open my $f2, '<', 'two.txt' or die $!;
      
      my %ids;
      
      while (<$f2>) {
        $ids{$1} = "$1_$2" if /^>(\S+)\s+(\d+)/;
      }
      
      open my $f1, '<', 'one.txt' or die $!;
      
      while (<$f1>) {
        s/^>(\S+)\s*$/>$ids{$1}/;
        print;
      }
      

      【讨论】:

        【解决方案4】:

        转过头来解决问题。在 perl 我会做这样的事情:

        #!/usr/bin/perl
        
        open(FH1, "one.txt");
        open(FH2, "two.txt");
        open(RESULT, ">result.txt");
        
        my %data;
        
        while (my $line = <FH2>)
        {
            chomp(line);
        
            # Delete leading angle bracket
            $line =~ s/>//d;
        
            # split enst and pos
            my ($enst, $post) = split(/\s+/, line);
        
            # Store POS with ENST as key
            $data{$enst} = $pos;
        }
        
        close(FH2);
        
        while (my $line = <FH1>)
        {
            # Check line for ENST
            if ($line =~ m/^>(ENST\d+)/)
            {
                my $enst = $1;
        
                    # Get pos for ENST
                my $pos = $data{$enst};
        
                    # make new line
                $line = '>' . $enst . '_' . $pos . '\n';
            }
        
            print RESULT $line;
        }
        
        close(FH1);
        close(RESULT);
        

        【讨论】:

        • 您应该使用三参数版本的open 和词法文件句柄。并且始终使用use strict;use warnings; 启动脚本。
        【解决方案5】:

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

        sed -n '/^$/!s|^\(\S*\)\s*\(\S*\).*|s/^\1.*/\1_\2/|p' two.txt | sed -f - one.txt
        

        【讨论】:

          【解决方案6】:

          试试这个 MATLAB 解决方案(无循环):

          %# read files as cell array of lines
          fid = fopen('one.txt','rt');
          C = textscan(fid, '%s', 'Delimiter','\n');
          C1 = C{1};
          fclose(fid);
          fid = fopen('two.txt','rt');
          C = textscan(fid, '%s', 'Delimiter','\n');
          C2 = C{1};
          fclose(fid);
          
          %# use regexp to extract ENST numbers from both files
          num = regexp(C1, '>ENST(\d+)', 'tokens', 'once');
          idx1 = find(~cellfun(@isempty, num));       %# location of >ENST line
          val1 = str2double([num{:}]);                %# ENST numbers
          num = regexp(C2, '>ENST(\d+)', 'tokens', 'once');
          idx2 = find(~cellfun(@isempty, num));
          val2 = str2double([num{:}]);
          
          %# construct new header lines from file2
          C2(idx2) = regexprep(C2(idx2), ' +','_');
          
          %# replace headers lines in file1 with the new headers
          [tf,loc] = ismember(val2,val1);
          C1( idx1(loc(tf)) ) = C2( idx2(tf) );
          
          %# write result
          fid = fopen('three.txt','wt');
          fprintf(fid, '%s\n',C1{:});
          fclose(fid);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-05-10
            • 1970-01-01
            • 2011-10-02
            • 2012-02-12
            • 1970-01-01
            • 1970-01-01
            • 2015-10-22
            • 1970-01-01
            相关资源
            最近更新 更多