我们如何使用perl对第1列和第2列用户数据进行排序

时间:2011-12-07 18:09:00

标签: perl

我是perl编程的新手。 我想读取文件数据,然后在第1列上排序记录,然后在第2列(删除重复记录)和存储的已排序记录到另一个文件中。以下是我的数据

第一列和第二列由制表符分隔

 user1 name       user2 name

    abc               xyz
    adc               xyz
    abc               xyz
    pqr               tyu
    xyz               abc
    tyu               pqr
    abc               pqr

在这个例子中,我希望首先对user1名称和user2名称进行排序记录,并且在排序时我想要删除重复记录。

输出应如下

user1 name        user2 name
  abc              pqr
  abc              xyz
  adc              xyz
  pqr              tyu
  tyu              pqr
  xyz              abc

请告诉我们如何实施这个perl?

4 个答案:

答案 0 :(得分:4)

#!/usr/bin/env perl
use strict;
use warnings;
my @list = <DATA>;
my $prev;
for (sort @list) {
    next if $prev && $_ eq $prev;
    $prev = $_;
    print;
}
__DATA__
    abc               xyz
    adc               xyz
    abc               xyz
    pqr               tyu
    xyz               abc
    tyu               pqr
    abc               pqr

答案 1 :(得分:1)

也许不是生产代码值得,但这是一种方法:

#!/usr/bin/perl

use strict;
use warnings;

my %seen;
print join "",
    grep {$_ !~ /^\s+$/ && !$seen{$_}++}
    sort {$a !~ /^ user/ <=> $b !~ /^ user/ || 
    $a cmp $b} <DATA>;

__DATA__
 user1 name       user2 name

    abc               xyz
    adc               xyz
    abc               xyz
    pqr               tyu
    xyz               abc
    tyu               pqr
    abc               pqr

输出:

 user1 name       user2 name
    abc               pqr   
    abc               xyz
    adc               xyz
    pqr               tyu
    tyu               pqr
    xyz               abc

这里最非常规的部分是$a !~ /^ user/ <=> $b !~ /^ user/排序条件。 $a !~ /^ user/评估除{1}之外的所有行的1(true),它将评估为0(false),因此标题首先放置,尾随行放到第二行排序条件,产生所需的结果。

答案 2 :(得分:1)

这完全取决于您存储数据的方式。我不确定您是否计划存储您的信息,因为您在课堂上可能会或可能没有了解参考资料。例如,如果您不知道引用,则可以执行以下操作:

my @array;
foreach my $value (<INPUT>) {
   chomp $value;
   my ($user1, $user2) = split (" ", $value);
   push (@array, "$user1:$user2");
}

这会将两个值存储为单个字符串。如果不了解参考文献,这是很常见的。

如果您了解参考文献,您可能会这样做:

my @array;
foreach my $value (<INPUT>) {
   chomp $value;
   my @line = split (" ", $value);
   push (@array, \@line);
}

我可以告诉你的是sort子程序允许你创建一个比较和排序值的函数。当您在sort中使用自己的函数时,会得到两个值$a$b,它们代表您要排序的值。您可以操纵这些,如果-1小于$a,则返回$b1如果$ a大于$b或返回零如果他们都是平等的。 Perl为您提供了两个运算符<=>cmp,使这更容易一些。

我们假设您将值存储为$user1:$user2,因为您尚未了解引用。您的排序例程可能如下所示。

sub sort {
    my ($a_col1, $a_col2) = split (/:/, $a);
    my ($b_col1, $b_col2) = split (/:/, $b);

    # Now we compare $a to $b. First, we can compare the
    # User 1 column:

    if ($a_col1 lt $b_col1) {
        return -1;    #$a < $b
    }
    elsif ($a_col1 gt $b_col1) {
        return 1;     #$a > $b
    }

    # If we're down here, it's because column 1 matches
    # for both $a and $b. We'll have to compare column #2
    # to see which one is bigger.

    if ($a_col2 lt $b_col2) {
       return -1;   #$a < $b
    }
    elsif ($a_col2 gt $b_col2) {
       return 1;    #$a > $b
    }

    #We're down here because both column #1 and column #2 match for both
    #$a and $b. They must be equal

    return 0;
}

现在,我的排序看起来像这样:

my @new_array = sort(\&sort, @array);

注意:这不是我个人这样做的方式。我可能会使用内置的cmp运算符并采用一些快捷方式。但是,我想把它拆分开来,所以你可以理解它。

顺便说一下,如果老师决定你应该在第一列之前对第二列进行排序,你可以通过改变小于和大于周围的符号来轻松修改你的sort子程序。


这是我的测试程序:

#! /usr/bin/env perl

use strict;
use warnings;

#Putting my data in `@array`

my @array;
foreach my $entry (<DATA>) {
    chomp $entry;
    my ($user1, $user2) = split " ",  $entry;
    push @array, "$user1:$user2";
}

# Sorting my data

my @new_array = sort \&sort, @array;

#Now printing out my data nice and sorted...

foreach my $element (@new_array) {
    my ($user1, $user2) = split (/:/, $element);
    print "$user1\t\t$user2\n";
}

#
# END OF PROGRAM
##################################################

##################################################
# Sort subroutine I'm using to sort the data
#
sub sort {
    my ($a_col1, $a_col2) = split (/:/, $a);
    my ($b_col1, $b_col2) = split (/:/, $b);

    # Now we compare $a to $b. First, we can compare the
    # User 1 column:

    if ($a_col1 lt $b_col1) {
        return -1;    #$a < $b
    }
    elsif ($a_col1 gt $b_col1) {
        return 1;     #$a > $b
    }

    # If we're down here, it's because column 1 matches
    # for both $a and $b. We'll have to compare column #2
    # to see which one is bigger.

    if ($a_col2 lt $b_col2) {
        return -1;   #$a < $b

   }
    elsif ($a_col2 gt $b_col2) {
        return 1;    #$a > $b
    }

    #We're down here because both column #1 and column #2 match for both
    #$a and $b. They must be equal

    return 0;
}

__DATA__
david       fu
david       bar
albert      foofoo
sandy       barbar
albert      foobar

答案 3 :(得分:0)

或者它可以如此简单:

print sort <DATA>;

__DATA__
    abc xyz
    pqr tyu
    xyz abc
    adc xyz
    tyu pqr
    abc pqr
    abc xyz

但是,只有你的数据如此简单。如果每列中的数据长度不同, 每列必须与最长的项目一样宽。像这样:

__DATA__
    abc              |xyz       |<-- other data in record...
    pqrwf            |tyu       |<-- other data in record...
    xyzsder          |abc       |<-- other data in record...
    adca             |xyzghrt   |<-- other data in record...
    tyuvdfcg         |pqr       |<-- other data in record...
    abcvfgfaqrt      |pqrbb     |<-- other data in record...
    abcaaaaaaaaaaa   |xyz       |<-- other data in record...

在这种情况下,简单排序仍然有效,但请注意,这些列填充了空格而非标签。