【问题标题】:How can we sort column 1 and then column 2 user data using perl我们如何使用 perl 对第 1 列和第 2 列用户数据进行排序
【发布时间】:2011-12-07 18:08:59
【问题描述】:

我是 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?

【问题讨论】:

    标签: perl


    【解决方案1】:
    #!/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
    

    【讨论】:

      【解决方案2】:

      这完全取决于您存储数据的方式。我不确定您打算如何存储您的信息,因为您在课堂上并且可能已经或可能没有了解参考资料。例如,如果你不知道引用,你可能会这样做:

      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,它们代表您正在排序的值。您可以操作这些,然后如果$a 小于$b1 如果$a 大于$b 则返回-1,或者如果它们都相等则返回零。 Perl 为您提供了两个运算符&lt;=&gt;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】:

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

        #!/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/ &lt;=&gt; $b !~ /^ user/ 排序条件。 $a !~ /^ user/ 对除第一行以外的所有行计算 1 (true),它将计算为 0 (false),因此将标题放在第一位,尾随行落入第二个排序条件,这会产生想要的结果。

        【讨论】:

          【解决方案4】:

          或者也可以这么简单:

          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...
          

          在这种情况下,简单排序仍然有效,但请注意这些列是用空格而不是制表符填充的。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-09-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-02-18
            相关资源
            最近更新 更多