perl 對文件排序經典實例

 perl 排序問題【實例】1

月份       電話號碼             市話      長途      總話費(元)
-----------------------------------------------------------------------
200809        74889999        3.0        6.6        188
200810        74885444        4.0        1.0        200
200810        74885555        0.8        5.5        120
200810        74889888        4.0        1.0        200

要求:1,按月份(從小到大)排序。
          2,按總花費(從大到小)排序。若總話費相等,則按號碼(從小到大)排序
          首先,按月份排序。同時,按總花費(從大到小)排序,若總話費相等,則按號碼(從小到大)排序

最佳答案:

print map {$_->[-1]}
    sort {$a->[0] <=> $b->[0] || $b->[1] <=> $a->[1] || $b->[2] <=> $a->[2]}
    map {[(split)[0,4,1],$_]} <DATA>;
__DATA__
200809        74889999        3.0        6.6        188
200810        74885444        4.0        1.0        200
200810        74885555        0.8        5.5        120
200810        74889888        4.0        1.0        200

也可以這樣:
#!/usr/bin/perl
use warnings;
use strict;
open (INFILE,"/home/liuguiyou/11.txt");

my @a=<INFILE>;
my @b;
@b= map {$_->[3]}
    sort {$a->[0] <=> $b->[0] ||$b->[1] <=> $a->[1] ||$b->[2] <=> $a->[2]}
    map {[(split)[0,4,1],$_]} @a;
close INFILE;
foreach (@b){
         print "$_";  

             }
注:
第一:@b= map {$_->[3]}  是因爲需要打印輸出的位置前面已經有了3個元素,在這裏可以寫爲@b= map {$_->[-1]},指的是從最後一個位置開始計算。
第二:sort {$a->[0] <=> $b->[0] ||$b->[1] <=> $a->[1] ||$b->[2] <=> $a->[2]}
            是因爲下面有三個條件,按照順序[0,4,1], 並且這些條件之間用||分開

任何可以從 $a 和 $b 中得到的有用信息都可以在一個排序過程中比較的基礎來用。比如,如果多行文本要根據特定域來排序,那麼可以在排序過程中使用 split 以獲取該域:


   @sorted_lines = sort {
      @a_fields = split /:/, $a;      # 冒號分隔的域
      @b_fields = split /:/, $b;
         
      $a_fields[3] <=> $b_fields[3]   # 對第四個域進行數字排序,然後
         ||
      $a_fields[0] cmp $b_fields[0]   # 對第一個域進行字串排序,然後
         ||
      $b_fields[2] <=> $a_fields[2]   # 對第三個域進行數字反向排序
      ...            # 等等
   } @lines;






perl
 排序問題【實例】2
#!/usr/bin/perl

@by_uid = map { $_->;[0]} sort {$a->;[1] <=>; $b->;[1] } map { [$_,(split /:/)[2]] } <DATA>;;
for(@by_uid){print ."/n";}

__DATA__
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
mailnull:x:47:47::/var/spool/mqueue:/dev/null
rpm:x:37:37::/var/lib/rpm:/bin/bash
ident:x:98:98:pident user:/:/sbin/nologin
pcap:x:77:77::/var/arpwatch:/sbin/nologin
sshd:x:501:501::/dev/null:/dev/null
mysql:x:100:101:MySQL server:/var/lib/mysql:/bin/bash
cnhackTNT:x:0:0::/home/cnhackTNT:/bin/bash
chutium:x:504:504:Seraph Chutium:/home/chutium/:/bin/bash

源代碼:
@by_uid = map { $_->;[0]} sort {$a->;[1] <=>; $b->;[1] } map { [$_,(split /:/)[2]] } <DATA>;;

以下對上面的根據uid來排序的code做出解釋:

第一:<DATA>;實際上可以看成一個數組,內容就是__DATA__下的那些,這裏我們把它當作是@DATA這個數組(Apile原文裏就是/etc下的passwd文件的內容)。

第二:map{[$_,(split /:/)[2]}從@DATA中取出每條數據,並對每條數據以 : 作爲分割符分割,也就是 split(/:/,$_);然後返回一個數組@_,以sshd:x:501:501::/dev/null:/dev/null爲例,那麼這個返回的數組中的一項可以看成:$_=['sshd:x:501:501::/dev/null:/dev/null','501'];
從上可看出這個$_是一個匿名的數組引用,他實際上包含兩個部分,其中第二部分是uid,也就是$_->;[1]
現在@_中的內容變成了:

....前面的省略.....
['rpm:x:37:37::/var/lib/rpm:/bin/bash','37']
['ident:x:98:98:pident user:/:/sbin/nologin,'98']
['pcap:x:77:77::/var/arpwatch:/sbin/nologin','77']
['sshd:x:501:501::/dev/null:/dev/null','501']
....後面的省略.....


第三:sort {$a->;[1] <=>; $b->;[1] }便是根據@_中的每項(實際上是數組引用)的第二部分uid的大小來排序,這裏的sort排序是標準的方法,很多書裏都有例子。
現在,經過上面的過程,數組@_中的內容已經以uid大小排好序了, 現在我們只要按@_排好的順序讀取每一項內容的第一部分,以['sshd:x:501:501::/dev/null:/dev/null','501']爲例,我們要取他的第一部分 'sshd:x:501:501::/dev/null:/dev/null',也就是$_->;[0],並將其保存到數組@by_uid中就好 了,而map { $_->;[0]}很好的完成了這項工作。
實際上只要明掌握了map,sort的用法就能容易的理解上面算法的意思
轉自http://bbs.chinaunix.net/archiver/
類似的問題:
http://bbs.chinaunix.net/archiver/?tid-596413.html
http://bbs.chinaunix.net/archiver/?tid-705277.html
http://bbs.chinaunix.net/archiver/?tid-1334818.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章