2011-07-10 28 views
3

我有一个制表符分隔符。由若干行和列组成的文本文件。我想更改前两列的内容,然后将修改后的文件写入新文件。
之前发生变化,每行的前两列是这个样子:Perl - 用制表符分隔的文本文件拆分列并用新值替换列的问题

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; 
+0

'my $ prot_id1 = $ split_col1 [length(@ split_col1)];'没有做你最想要的 - 可能是数组的最后一个索引。 'length'返回字符串中的字符数。要索引数组中的最后一个元素,只需使用'my $ prot_id1 = $ split_col1 [-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; 

    } 
+0

我应该添加 - 如果在每列中查找的部分之间有任何空格(例如Q96PU5)和选项卡然后这个正则表达式不匹配。当我将您的示例数据复制并粘贴到文件中时,我意外地在其中放置了一个空间,但没有找到匹配项。我认为改变正则表达式为:/:((\ w | \ d)*)\ s * $ /会解决这个问题。 (\ s *匹配零个或多个空白字符,但是由于它位于原子之后和字符串末尾的'$'标记之前,空白将不会包含在匹配中。) – ratsbane

+0

感谢您的确如此上班。我不太明白这部分是做什么的: –

+0

〜/ :((\ w | \ d)*)$/ –

1

ratsbane的回答很不错,但你可能想工作小时后才知道为什么你得到了你所做的答案。原因是$ col1中有一个管道。这是一个正则表达式中的“OR”。所以,当你试图代替正则表达式$ COL1,你在做一个查找和替换在

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

现在,作为一个正则表达式,这是什么搭配?它只

dip:DIP-41935N 

匹配,从而是什么得到了更换!

希望有帮助!

+0

啊,很酷。我从来没有停下来看看*为什么*他的代码不工作。忘记逃避模式是很容易的。 – ratsbane

+0

很高兴知道,我只是无法解决它!所以没有办法根据我已有的代码使用代码来解决专栏中的'管道'问题? –

+0

是的,你可以通过写这个来解决这个问题:'s/\ Q $ col1 \ E/$ prot_id1 /;'(当然对于第2列是相同的)。但最好还是采用@TLP的解决方案。 –

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"; 
} 

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

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

然后我分割空白。你可以在选项卡上分割。除非列中嵌入了空格,否则任何一种方式都可以。然后我遍历这两列,匹配并捕获列结尾处不是冒号的每一行内容。或者以另一种方式表达,即最后一个冒号后的所有内容。我将结果捕获到$ column变量中,该变量将@columns中的相应元素进行了别名化。这样,当我完成@columns只包含我的捕获。

最后,在处理完两列之后,我们将$“本地化”,为其分配一个制表符。这样,当我们通过将@columns用引号引起来打印两列时,插值会自动在列之间再次粘贴一个制表符如果你喜欢一个不同的角色,你现在知道在哪里可以改变它。

然后到下一行while循环运动。任何空行会被跳过。

请参阅perldoc开放,perlretut,perlvar,和perlop解释三参数开放以及词法文件句柄,正则表达式的解释,Perl的特殊变量(如$“),以及引用插值如何工作。

好问题!

2

已经有一些体面的答案,但我想给你看一个更简单的解决方案。该脚本,你会使用这样的:

$ script.pl DIP.txt > DIP_changed.txt 

和脚本本身其实只是:

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

它并不需要比这更复杂。

+0

D'oh。我比我的回答更好。 – ratsbane

+0

看起来很简单!这不会改变包含'uniprot:'的文件中的每一列,而不仅仅是前两列? –

+0

@James不,一个正则表达式匹配只会执行一次,除非添加'/ g'(全局)选项。所以这两个将会改变前两场比赛,而不会有更多。 – TLP

相关问题