Perl - 在制表符分隔的文本文件中拆分列并使用新值替换列时出现问题

时间:2011-07-10 02:49:21

标签: perl file split substitution amend

我有一个标签分隔符。文本文件由许多行和列组成。我想更改前两列的内容,然后将修改后的文件写入新文件 在更改之前,每行的前两列看起来像这样:

COLUMN1:                                              
dip:DIP-41935N|refseq:NP_056092|uniprotkb:Q96PU5    

COLUMN2:    dip:DIP-48957N|uniprotkb:P49281

我希望它们只包含每列末尾的id号,所以我希望它们如下:

COLUMN1:        Q96PU5          

COLUMN 2:       P49281

我已在选项卡中拆分行以获取各列。然后拆分前两列以获取所需的ID号($ prot_id)。然后我尝试用ID代替第1列和第2列的内容。但是更改后的文件中的输出并不像我预期的那样。它看起来像这样:

  COLUMN1:                                           
Q96PU5|refseq:NP_056092|uniprotkb:Q96PU5    

COLUMN 2:
P49281|uniprotkb:P49281

只是列的第一部分已被替换。我已经玩了好几个小时,无法弄清楚我做错了什么。任何帮助非常感谢。 我的代码如下:

#!/usr/bin/perl  

use warnings;
use strict;


my $file = 'DIP.txt';

open(INFILE, $file) or die "Can't open file: $!\n";
open(my $outfile, '>', 'DIP_changed.txt'); 
my @lines = <INFILE>;


foreach $_ (@lines) {
    my @columns = split('\t', $_);

            my $col1 = $columns[0];
            my $col2 = $columns[1];


            my @split_col1 = split ('uniprotkb:', $col1);
            my @split_col2 = split ('uniprotkb:', $col2);

            my $prot_id1 = $split_col1[length(@split_col1)];
            my $prot_id2 = $split_col2[length(@split_col2)];

            print $prot_id1, "\n";

             s/$col1/$prot_id1/;
             s/$col2/$prot_id2/;

            print {$outfile} $_; 
}



exit;

4 个答案:

答案 0 :(得分:2)

已经有了不错的答案,但我想向您展示一个更简单的解决方案。这个脚本,您可以这样使用:

$ script.pl DIP.txt > DIP_changed.txt

脚本本身就是:

while (<>) {
    s/\S+uniprotkb:(\S+)/$1/;
    s/\S+uniprotkb:(\S+)/$1/;
    print;
}

它不需要比那更复杂。

答案 1 :(得分:1)

尝试这样的事情:

这是一个简洁的Perl习语 - 匹配像这样的正则表达式上的字符串

$columns[0]=~/:((\w|\d)*)$/;

(注意那里用括号定义了两个原子)并将匹配的结果(无论是第1,第2等原子)分配给数组 - 还是分配给一组标量变量。数组列表,如下所示:

($columns[0]) = $columns[0]=~/:((\w|\d)*)$/;

看,你是在正确的轨道,但你正在使它变得比它需要的更难:)

#!/usr/bin/perl  

use warnings;
use strict;

my $file = 'DIP.txt';

open(INFILE, $file) or die "Can't open file: $!\n";
open(my $outfile, '>', 'DIP_changed.txt');


foreach my $line (<INFILE>) {
    print "The input line is $line\n";
    my @columns = split('\t', $line);

    ($columns[0]) = $columns[0]=~/:((\w|\d)*)$/;
    ($columns[1]) = $columns[1]=~/:((\w|\d)*)$/;

    printf  "The output line is  %s\n", join ',', @columns;
    printf  $outfile join ',', @columns;

    }

答案 2 :(得分:1)

ratsbane的回答非常好,但是你可能想知道工作时间为什么你得到了答案。原因是$ col1中有一个管道。这是正则表达式中的“OR”。因此,当您尝试替换正则表达式$ col1时,您正在执行查找并替换

dip:DIP-41935N|refseq:NP_056092|uniprotkb:Q96PU5

现在作为正则表达式,它匹配什么?它仅匹配

dip:DIP-41935N

所以 是被取代的东西!

希望有所帮助!

答案 3 :(得分:1)

可能没有什么理由在开头啜饮文件,而不是逐行处理。逐行处理将更好地扩展。考虑到这一点,我会这样做:

use warnings;
use strict;


my $file = 'DIP.txt';

open my $in_fh, '<', $file or die $!;
open my $out_fh, '>', 'new' . $file or die $!;

while ( <$in_fh> ) {
    chomp;
    next unless length $_; # Skip blank lines.
    my ( @columns ) = split /\s+/, $_; # Split on whitespace (you may prefer \t).
    foreach my $column ( @columns ) {
        ( $column ) = $column =~ m{([^:]+)$};
    }
    local $" = "\t";
    print $out_fh "@columns\n";
}

首先,它在输入文件和输出文件中使用open的三个arg版本。这是一个很好的习惯。接下来,它使用词法文件句柄而不是旧的fileglob文件句柄。当Lexical超出范围时自动关闭,并且不会成为全局符号表的一部分。

接下来,脚本会读取文件并逐行处理,以避免啜饮。如果文件可能变大,或者您处于内存使用率非常高的环境中,这可能是有利的。除非你有充分的理由去啜饮,否则也可能养成不这样做的习惯。

然后我分裂了空白。您可以拆分选项卡。除非列中嵌入了空格,否则两种方式都有效。然后我遍历两列,匹配并捕获不是冒号的列末尾的每个内容。或另一种方式,即最后一次冒号之后的所有事情。我将结果捕获回$ column变量,该变量将@columns中的相应元素别名化。这样,当我完成时@columns只保存我的捕获。

最后,在处理了两列之后,我们对$“进行了本地化,为其分配了一个制表符。这样,当我们通过在引号中包装@columns来打印两列时,插值会自动在列之间再次添加制表符。如果你喜欢不同的角色,你现在知道在哪里改变它。

然后while循环移动到下一行。将跳过任何空白行。

请参阅perldoc open,perlretut,perlvar和perlop,以解释三个arg open和lexical文件句柄,regexp的解释,Perl的特殊变量,如$“,以及引用插值如何工作。

好问题!