如何使用linux或perl从一行中获取uniq列值?

时间:2017-02-18 10:02:58

标签: perl unique

我有一个如下文件

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;

但它没有用。 有人可以帮忙吗?

2 个答案:

答案 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

请参阅Command switches in perlrun

答案 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