2011-07-10 29 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

相關問題