我有一个如下文件
1 B B C D B
2 K B D D K
1 B B C D B
2 K B D D K
1 B B C D B
2 K B D D K
我希望输出看起来像这样
1 B C D
2 K B D
1 B C D
2 K B D
1 B C D
2 K B D
排序命令不起作用,所以我尝试编写Perl程序 -
use strict;
use Data::Dumper;
my $file1 = <$ARGV[0]>;
open (IF2, "$file1") || die "Cannot open the file\n";
open (OUT, ">$file1.out") || die "Cannot open the out file\n";
my $k = 0;my $i=0;
my @line;
my $m;
my @line2;
while ( chomp($m = <IF2>) ) {
my $count = 0;
@line2 = split(/\t/,$m);#<stdin>;
my $l = length @line2;print $l;<stdin>;
for (my $x = 0; $x < $l;$x++) {
my $k = 0;
for (my $y = 0;$y < $l; $y) {
$i++;
#
if ($count == 0)
{
print OUT "\t$line2[$x]";
$count++;
}
if ($count != 0 && $x != $y)
{
if ($line2[$x] eq $line2[$y])
{
$k++;
}
}
}
if ($k == 0)
{
print OUT "\t$line2[$x]";
}
}
print OUT "\n";
}
print $i;
close IF2;
close OUT;
但它没有用。 有人可以帮忙吗?
答案 0 :(得分:4)
注意问题中的输入和输出示例已经过编辑:现在数据与标题一致,要求删除所有重复项。 (请参阅最初的问题,看看它是什么样的。)我也要留下原始问题的答案,直到我们从海报中听到。
我想首先发表一般性评论。
您发布的代码是在Perl中编写C风格程序的诚实尝试。我建议抵制这一点并学习如何使用Perl 。它需要花费一点时间和精力投入,但它会很快并且快速得到回报。正如您在下面所看到的,它使许多事情变得无比容易。
即便如此,发布的代码也存在问题,但我现在无法编写代码审查。
编辑问题
我们需要从每一行删除所有重复项。一种方法:用空格分割行,然后从列表中删除重复项,这是一个标准的任务,有现成的解决方案。
use warnings;
use strict;
use List::MoreUtils qw(uniq);
my $file = '...';
my $fileout = '...';
open my $fh, '<', $filen or die "Can't open $file: $!";
open my $fh_out, '>', $fileout or die "Can't open $fileout: $!";
while (<$fh>)
{
my @unique = uniq split;
print $fh_out "@unique\n";
}
close $fh;
close $fh_out;
当在元素之间插入空格(或$"
中的任何内容)时,通过打印带引号的数组来恢复带空格的行。另一个选项是join结果列表
my $res = join ' ', uniq split;
print $fh_out $res, "\n";
或只是print $fh_out join(' ', uniq split), "\n";
。
这使用List::MoreUtils模块中的uniq
。来自uniq
的注释
返回列表中元素的顺序与LIST中的相同。
提到List::MoreUtils
后,请注意核心模块List::Util。
单线版
perl -MList::MoreUtils=uniq -lne'print join " ", uniq split' input > output
或
perl -MList::MoreUtils=uniq -lane'print join " ", uniq @F' input > output
请参阅Command switches in perlrun
原始问题(请参阅编辑历史记录中)
我回答这个问题,认为这是输入
1 B B C D B 2 K B D D K 1 B B C D B 2 K B D D K 1 B B C D B 2 K B D D K
这是期望的输出
1 B C D 2 K B D 1 B C D 2 K B D 1 B C D 2 K B D
根据您想要的输出,您只想删除相邻的重复项(而不是标题所示的“ uniq ”)。
为此,您可以使用正则表达式匹配重复模式的功能,方法是使用backreferences。首先我们需要剥离所有空间,然后我们将它们放回去。例如
use warnings;
use strict;
my $file = '...';
my $fileout = '...';
open my $fh, '<', $filen or die "Can't open $file: $!";
open my $fh_out, '>', $fileout or die "Can't open $fileout: $!";
while (my $line = <$fh>)
{
$line =~ s/\s*//g; # remove spaces /
$line =~ s/(.)\1+/$1/g; # remove adjacent duplicates
$line =~ s/(.)/$1 /g; # restore space
print $fh_out $line;
}
close $fh;
close $fh_out;
.
匹配任何字符,如果需要,可以使用更严格的内容(例如\w
)替换为“单词”字符。见perlretut。请注意,我们无法像替换一样恢复空间(如s/(.)\1+/$1 /g
),因为非重复的字符不匹配,并且不会返回空格。
这可以通过更简洁的方式完成。
单线版
perl -pe's/\s*//g; s/(.)\1+/$1/g; s/(.)/$1 /g' input > output
答案 1 :(得分:2)
我建议这样的事情。它查找所有出现的空白后跟非空格,并检查当前行之前是否已经看到非空格。如果之前已经看到非空白,则删除匹配的子字符串,否则保持不变
use strict;
use warnings 'all';
while ( <DATA> ) {
my %seen;
s/(\s+(\S+))/ $seen{$2}++ ? '' : $1 /eg;
print;
}
__DATA__
1 B B C D B
2 K B D D K
1 B B C D B
2 K B D D K
1 B B C D B
2 K B D D K
1 B C D
2 K B D
1 B C D
2 K B D
1 B C D
2 K B D
这可以在像这样的单行中完成
perl -pe 'my %s; s/(\s+(\S+))/ $s{$2}++ ? "" : $1 /eg' myfile