我在使用Perl时非常新,我需要用它来比较文件的所有行。该文件有两个以|分隔的ID和每个ID对的值。它看起来像这样:
a|b 9
a|a 1
a|c 4
s|c 3
f|e NA
a|d 2
d|a 2
d|b 5
c|l NA
c|s 3
如果有另一个具有相同ID(字母)的行,我想消除一行,但是按顺序排列(如| d和d | a),我将“NA”作为值的行和两个位置具有相同ID的行(如a | a 1)。 从这里的例子中,我想获得这样的输出:
a|b 9
a|c 4
s|c 3
a|d 2
d|b 5
我正在尝试编写的代码。它可以消除带有“NA”的行和ID相同的行(如| a 1),但是它无法检测具有反转ID的行。
$file = "test.txt";
open (HAN, "$file") || die "No input file";
@r = <HAN>;
close (HAN);
for ($i=0; $i<=$#r; $i++) {
chomp($r[$i]);
($id, $v) = split (/\t/, $r[$i]);
if ( $v ne NA ) {
($id1, $id2) = split (/\|/, $id);
$ii = $id1."|".$id2;
$dd = $id2."|".$id1;
if(($id1 ne $id2)||($ii ne $dd)){
print "$id\t$v\n";
}
}
}
绝对欢迎任何帮助!
提前谢谢你, 加布
答案 0 :(得分:1)
要解决此问题,您需要跟踪到目前为止遇到的所有对(ID)。处理新行时,需要与ID对列表匹配,以查找它是否为反对。
以下修改使其有效:
$file = "test.txt";
open (HAN, "$file") || die "No input file";
@r = <HAN>;
@encountered;
close (HAN);
for ($i=0; $i<=$#r; $i++) {
chomp($r[$i]);
$present=0;
$invertPr=0;
($id, $v) = split (/\t/, $r[$i]);
if ( $v ne NA ) {
($id1, $id2) = split (/\|/, $id);
if($id1 eq $id2) {
next;
}
for($j = 0; $j < $#encountered; $j+=2) {
if($encountered[$j] eq $id1 && $encountered[$j+1] eq $id2) {
$present = 1;
}
if($encountered[$j+1] eq $id1 && $encountered[$j] eq $id2) {
$invertPr = 1;
}
}
if($present == 0) {
push(@encountered, $id1);
push(@encountered, $id2);
}
if($invertPr == 0) {
print "$id\t$v\n";
}
}
}
答案 1 :(得分:0)
以下脚本始终使用“lower”id作为密钥的第一部分。因此,您不必关心倒置ID:
#!/usr/bin/perl
use warnings;
use strict;
sub compare {
my %result;
for (@_) {
my ($id1, $id2, $value) = /(.+)\|(.+) (.+)/;
next if $id1 eq $id2 or 'NA' eq $value;
($id1, $id2) = sort $id1, $id2;
next if exists $result{"$id1|$id2"};
$result{"$id1|$id2"} = $value;
}
return join "\n", map "$_ $result{$_}", keys %result;
}
print compare(<DATA>);
__DATA__
a|b 9
a|a 1
a|c 4
s|c 3
f|e NA
a|d 2
d|a 2
d|b 5
c|l NA
c|s 3
答案 2 :(得分:0)
另一种方法,如果之前的2个回复让您感到困惑:
#!/usr/bin/perl
use warnings;
use strict;
my %previous;
open (my $IN,'<','file.txt') or die "$!";
while (<$IN>) {
my ($tmp,$v)=split/ /;
next if $v=~/NA/; #remove the rows in which I have "NA" as value
my ($id1,$id2)=split/\|/,$tmp;
next if $id1 eq $id2; #remove the rows with the same ID in both positions
next if exists $previous{"$id2|$id1"}; #remove the row if there is another with the same IDs (letters), but in an inverted order
$previous{$tmp}=1;
print;
}
close $IN;